I'm having some issues trying to figure out Async communication on a serialport object and was hoping to get some help. I'm still learning Async stuff so be gentle:)
I basically need to make sure that the buffer isn't full prior to writing to the serial port and also all data has been read before any more data gets written to avoid packet collision. Unfortunately the microcontroller that I am writing to does not have any handshaking or CTSEnabled so I have to mimic it in code.
public partial class Form2 : Form
{
System.IO.Ports.SerialPort port = new System.IO.Ports.SerialPort();
System.Timers.Timer tmrPolling = new System.Timers.Timer(200);
static byte [] inputPacket = new byte[2] { (byte)255, (byte)0 }; // Byte to send to SerialPort when input polling timer is activated.
AsyncCallback callback;
IAsyncResult result;
Form2()
{
InitializeComponent();
port.PortName = "COM9";
port.BaudRate = 38400;
port.DataBits = 8;
port.StopBits = System.IO.Ports.StopBits.One;
port.Parity = System.IO.Ports.Parity.None;
port.ReadTimeout = 1;
port.ReceivedBytesThreshold = 5;
port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);
callback = new AsyncCallback(ReadComplete);
port.ReadBufferSize = (byte)60;
port.WriteBufferSize = (byte)60;
port.Open();
tmrPolling.Elapsed += new System.Timers.ElapsedEventHandler(tmrPolling_Elapsed);
}
void WritePacket(byte[] packet)
{
result = port.BaseStream.BeginWrite(packet, 0, packet.Count(), callback, port); // writes the input packet to the SerialPort - needed to read the IO values
}
void ReadComplete(IAsyncResult result)
{
}
void tmrPolling_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
WritePacket(inputPacket); // writes the input packet to the SerialPort - needed to read the IO values
}
void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
port.BaseStream.EndWrite(result);
// Reads the data coming in from the serial port and calls the thread safe delegate to update the values on the form.
byte [] received = new byte[port.BytesToRead];
result = port.BaseStream.BeginRead(received, 0, received.Count(), callback, port);
port.BaseStream.EndRead(result);
}
private void UpdateValues()
{
byte[] b = new byte[] { (byte)255, (byte)6, (byte)hsbPan.Value, (byte)vsbTilt.Value, (byte)0,
(byte)0, (byte)0, (byte)0, (byte)13, (byte)10 };
WritePacket(b);
}
private void chkEnablePolling_CheckedChanged(object sender, EventArgs e)
{
tmrPolling.Enabled = chkEnablePolling.Checked;
test.Enabled = chkEnablePolling.Checked;
vsbInputInterval.Enabled = chkEnablePolling.Checked;
vsbInputInterval.Value = Convert.ToInt32(tmrPolling.Interval);
txtInputInterval.Enabled = chkEnablePolling.Checked;
txtInputInterval.Text = ((float)tmrPolling.Interval / ( float)1000).ToString() + " sec";
}
private void vsbInputInterval_Scroll(object sender, ScrollEventArgs e)
{
// adjusts the tick frequency of the Timer for input polling.
txtInputInterval.Text = ((float)tmrPolling.Interval / (float)1000).ToString() + " sec";
tmrPolling.Interval = vsbInputInterval.Value;
}
private void vsbTilt_Scroll(object sender, ScrollEventArgs e)
{
UpdateValues();
}
private void hsbPan_Scroll(object sender, ScrollEventArgs e)
{
UpdateValues();
}
}
Related
I have the Arduino who send the data continuously without caring the delay and i want to put the delay on my display in C#, so i can print all the value without missing a thing but in every line i want to put a delay for (let say) two seconds. I did with Thread.Sleepsomehow it blocked and delayed my UI and Task.Delay won't help. Does anyone knows how to fix this?
My code :
private SerialPort mySerialPort;
private readonly StreamWriter sw = new(#"D:\MAAT\readcoba.csv");
public MainWindow()
{
InitializeComponent();
Configure();
}
private void Configure()
{
mySerialPort = new(Port())
{
BaudRate = 9600,
Parity = Parity.None,
StopBits = StopBits.One,
DataBits = 8,
};
mySerialPort.DataReceived += DataReceivedHandler;
mySerialPort.Open();
}
private string Port()
{
string port = "";
string[] ports = SerialPort.GetPortNames();
foreach (string x in ports)
{
port = x;
}
return port;
}
private async void DataReceivedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
// It's still not work
await Task.Delay(2000);
string indata = mySerialPort.ReadExisting();
string[] arrData = indata.Split(',');
Application.Current.Dispatcher.Invoke(() =>
{
foreach (string item in arrData)
{
raw_data_label.Content = raw_data_label.Content.ToString().Contains("Nothing to Display") ?
$"{item}" : $"{raw_data_label.Content}" + $"{item}";
}
sw.WriteLine(indata);
});
}
private void WindowClosed(object sender, EventArgs e)
{
sw.Close();
mySerialPort.Close();
}
}
Instead of invoking the UI thread (which you don't have the control te limit). Put the data into a variable/field and use a DispatcherTimer to display it. This way you control the update speed on the screen.
Here's an example:
private SerialPort mySerialPort;
private readonly StreamWriter sw = new(#"D:\MAAT\readcoba.csv");
// create a dispatcher timer
private DispatcherTimer timer = new DispatcherTimer(TimeSpan.FromMilliseconds(200), DispatcherPriority.Normal, UpdateLabel, Dispatcher.CurrentDispatcher)
// a lock object to use threadsafe object access
private object lockObject = new Object();
private static void UpdateLabel(object sender, EventArgs e)
{
// never update UI within a lock (which is used on other threads)
// Create a copy or if the field is only written (on the other end) copy the reference.
string[] data;
lock(lockObject)
data = _arduinoData; // You don't need to create a copy, but that's only because the _arduinoData isn't used.
foreach (string item in data)
{
// ????? why write to the same label over and over? Rather use a StringBuilder
raw_data_label.Content = raw_data_label.Content.ToString().Contains("Nothing to Display") ?
$"{item}" : $"{raw_data_label.Content}" + $"{item}";
}
}
public MainWindow()
{
InitializeComponent();
Configure();
}
private void Configure()
{
mySerialPort = new(Port())
{
BaudRate = 9600,
Parity = Parity.None,
StopBits = StopBits.One,
DataBits = 8,
};
mySerialPort.DataReceived += DataReceivedHandler;
Thread.Sleep(2000);
mySerialPort.Open();
}
private string Port()
{
string port = "";
string[] ports = SerialPort.GetPortNames();
foreach (string x in ports)
{
port = x;
}
return port;
}
private string[] arduinoData;
private void DataReceivedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
// It's still not work
// await Task.Delay(2000); <-- don't delay here!
string indata = mySerialPort.ReadExisting();
string[] arrData = indata.Split(',');
sw.WriteLine(indata);
// lock and assigny the reference to the field (this is only allowed when the reference isn't used anymore _(if the string array isn't yours, Use a ToArray to create a copy)_
// That's why the s.WriteLine(..) is move above this.
lock(lockObject)
arduinoData = arrData;
}
private void WindowClosed(object sender, EventArgs e)
{
sw.Close();
mySerialPort.Close();
so im new in C# Programming. I have programmed a C# Forms Application in Visual Studio to communicate to/initialze a device over the Serial Port. The communication between the device is COBS coded, so there are no 0x00 bytes except on the end of each message. The messages sent and received have different length.
My problem is at the moment, that the Messages I receive are not complete or start at the middle of a message, so i cannot trigger sent messages on a specific value in the received messages. You can determine end of a message with received 0x00 (0x00 means end of message in COBS coded data)
So what i need is something to handle the complete message and put it in a byte array to analyze i.e. byte[11] for a specific value.
Here is what ive done so far:
private bool b_portopen = false;
private byte[] b_rcv_buffer = new byte[256];
private void button1_Click(object sender, EventArgs e) {
//InitTimer();
if (b_portopen == false)
{
serialPort1.PortName = comboBox1.SelectedItem.ToString();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
serialPort1.Open();
b_portopen = true;
button1.Text = "Close";
button2.Enabled = true;
Console.WriteLine("Serial Port Opened");
}
else if (b_portopen == true)
{
serialPort1.Close();
b_portopen = false;
button1.Text = "Open";
button2.Enabled = false;
Console.WriteLine("Serial Port Closed");
}
}
private async void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
serialPort1.Read(b_rcv_buffer, 0, 256);
//serialPort1.Read(b_rcv_buffer1, 11, 2);
richTextBox1_receive.Invoke(new Action(() =>
{
richTextBox1_receive.AppendText(BitConverter.ToString(b_rcv_buffer) + "\n");
richTextBox1_receive.ScrollToCaret();
}));
switch (b_rcv_buffer[10])
{
case b_state_startup:
do something
case b_state_be_start_conf:
do something
case b_state_keepalive_conf:
do something
case b_state_unprepare_conf:
do something
case b_state_prepare_conf:
do something
}
}
So, i found a solution with using ConcurrentQueue:
ConcurrentQueue<byte> b_rcv_buffer = new ConcurrentQueue<byte>();
private Timer timer2;
public void InitTimer()
{
timer2 = new System.Windows.Forms.Timer();
timer2.Tick += new EventHandler(timer2_Tick);
timer2.Interval = 1; // in miliseconds
timer2.Start();
}
private async void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
int bytes = serialPort1.BytesToRead;
byte[] buf = new byte[bytes];
serialPort1.Read(buf, 0, serialPort1.BytesToRead);
for(int i = 0; i < buf.Length; i++)
{
b_rcv_buffer.Enqueue(buf[i]); //Enqueue every received Byte in Concurrentqueue
}
}
private async void timer2_Tick(object sender, EventArgs e)
{
if (b_rcv_buffer.Contains<byte>(0x00))
{
byte[] array = b_rcv_buffer.ToArray();
richTextBox1_receive.Invoke(new Action(() =>
{
richTextBox1_receive.AppendText(BitConverter.ToString(array) + "\n");
//richTextBox1_receive.ScrollToCaret();
}));
byte ignored;
while (b_rcv_buffer.TryDequeue(out ignored));
}
I have the below code in WPF application.
It reads the strings from the serial port and displays in textbox.
I want to execute an operation according to the serial input.
This I'm thinking to do either by reading the string variable (RData) or by reading the textbox.text.
I've tried with if statements in the functions but it doesn't work. Please help.
public partial class MainWindow : Window
{
SerialPort serial = new SerialPort();
string RData;
public MainWindow()
{
InitializeComponent();
getPorts();
}
void getPorts()
{
String[] ports = SerialPort.GetPortNames();
comboBoxPorts.ItemsSource = ports;
}
private void buttonConnect_Click(object sender, RoutedEventArgs e)
{
serial.PortName = comboBoxPorts.Text;
serial.BaudRate = 9600;
serial.DataBits = 8;
serial.Handshake = System.IO.Ports.Handshake.None;
serial.StopBits = StopBits.One;
serial.ReadTimeout = -1;
serial.WriteTimeout = -1;
serial.Open();
if (serial.IsOpen)
{
buttonConnect.IsEnabled = false;
labelConState.Content = "Connected";
labelConState.Foreground = System.Windows.Media.Brushes.Green;
serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(Recieve);
}
}
private delegate void UpdateUiTextDelegate(string text);
private void Recieve
(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
RData = serial.ReadExisting();
Dispatcher.Invoke(DispatcherPriority.Send, new UpdateUiTextDelegate(WriteData), RData);
}
private void WriteData(string text)
{
textBoxPrompt.Text += text;
}
}
You should set parity property of SerialPort.
Need help with CAS CL5200 scale.
i have already seen all the posts regarding this problem , but none of them are working regarding CAS CL5200 scale- connected with USB cable that creates a virtual com port (COM41)
here are the steps i followed -
To test the communication, I installed CL-Works (scales software for some functions) --> working.
Installed Free Device Monitoring Studio to see all the connection params.
Tested with hyperterminal --> not working
Free Device Monitoring Studio reports that "W" is written to com port but nothing returns.
The code here is what I found on other related post, tested it with byte[] and string, either way the code writes on COM41 "W" but there is nothing to read.
Code:
public partial class Form1 : Form
{
private const int BaudRate = 19200; // BaudRate Constant
private SerialPort _serialPort;
private void button1_Click_1(object sender, EventArgs e)
{
if (_serialPort != null && _serialPort.IsOpen)
_serialPort.Close();
if (_serialPort != null)
_serialPort.Dispose();
_serialPort = new SerialPort("COM41", BaudRate, Parity.None, 8, StopBits.One); // Creates new SerialPort using the name selected in the combobox
_serialPort.Handshake = Handshake.None;
// _serialPort.DataReceived += SerialPortOnDataReceived; // this event happens everytime when new data is received by the ComPort
_serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
_serialPort.RtsEnable = true;
_serialPort.DtrEnable = true;
_serialPort.Open();
textBox1.Text = "Listening on " + _serialPort.PortName + "...\r\n";
// byte[] towrite = GetBytes("W"); // get the weight from scale wiyh "W" as byte[]
// _serialPort.Write(towrite,0,1);
_serialPort.WriteLine("W");
}
private delegate void Closure();
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (InvokeRequired) // makes sure the function is invoked to work properly in the UI-Thread
BeginInvoke(new Closure(() => { _serialPort_DataReceived(sender, e); })); //<-- Function invokes itself
else
while (_serialPort.BytesToRead > 0) //<-- repeats until the In-Buffer is empty
{
//textBox1.Text += string.Format("{0:X2} ", _serialPort.ReadByte());
textBox1.Text += _serialPort.ReadExisting();
//<-- bytewise adds inbuffer to textbox
}
}
private byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
}
If you can't see the data in the hyperterminal, you can't see it in the code you wrote. See it there first. then try this
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
Get_Data(serialPort1.ReadLine().ToString());
}
private void Get_Data(string Result_Data)
{
Application.DoEvents();
if (!InvokeRequired)
{
try
{
double dNew = Convert.ToDouble(Result_Data) / 100;
lbl_weight.Text = dNew.ToString("N2");
}
catch (Exception err)
{
MessageBox.Show(err.ToString());
}
}
else { Invoke((MethodInvoker)(() => Get_Data(Result_Data))); }
Application.DoEvents();
}
public void Connection_Start()
{
string comport = "COM1";
serialPort1.DataReceived += new
SerialDataReceivedEventHandler(serialPort1_DataReceived);
serialPort1.PortName = comport;
serialPort1.BaudRate = 9600;
serialPort1.Parity = Parity.None;
serialPort1.DataBits = 8;
serialPort1.StopBits = StopBits.One;
serialPort1.DtrEnable = true;
serialPort1.Open();
}
I am trying to send ASCII caracters to a VideoJet Excel 170i printer, via a RS-232 cable (Serial)
When I am using the test program, i have no problem getting a response from the printer, i can change the status of the printer.
This is the code i've made
public partial class Form1 : Form
{
private SerialPort port = new SerialPort("COM1");
private delegate void SetTextDeleg(string data);
public Form1()
{
InitializeComponent();
}
private void addtoText(string text)
{
this.richTextBox1.Text = this.richTextBox1.Text + "\n" + text;
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
port.BaudRate = 9600;
port.Parity = Parity.None;
port.StopBits = StopBits.One;
port.DataBits = 8;
port.Handshake = Handshake.None;
port.ReadTimeout = 2000;
port.WriteTimeout = 500;
port.DtrEnable = true;
port.RtsEnable = true;
port.Open();
port.DataReceived += DataReceivedHandler;
addtoText("Port is ready");
}
catch (Exception ex)
{
//Console.WriteLine("Error opening my port: {0}", ex.Message);
addtoText("Error opening my port: {0}" + ex.Message);
}
}
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
System.Threading.Thread.Sleep(500);
string indata = sp.ReadExisting();
this.BeginInvoke(new SetTextDeleg(DisplayToUI), new object[] { indata });
}
private void DisplayToUI(string displayData)
{
addtoText(displayData);
}
private void button1_Click(object sender, EventArgs e)
{
port.Write(tbxAscii.Text);
}
private void Form1_Leave(object sender, EventArgs e)
{
port.Close();
}
}
I need to sent ASCII caracters to the printer, like
[1B][01][09]
To turn the printer to Print Mode.
The printer is supposed to respond, i get no response, and the printer doesn't change its status.
I have a program made to test the serial connection made by the printer, and i can see that all the settings are OK (Baud rate, Parity... port), and indeed on port COM1.
So i think that my port.write is not sending any info to the printer... or maybe i'm sending corrup info and i'm not reading the response of the printer.
are you sure that you want to send [1B][01][09] or do you want to send that byte sequence 0x1b,0x01,0x09
just to see if this works, send the following in you click handler
private void button1_Click(object sender, EventArgs e)
{
var bytes = new byte[] { 0x1b, 0x01, 0x09 };
port.Write(bytes, 0, bytes.Length);
port.Flush(); // make sure everything is written
}
reading has to be changed, to handle bytes
SerialPort sp = (SerialPort)sender;
System.Threading.Thread.Sleep(500);
var available = sp.BytesToRead; // check how many bytes are ready to be read
if (available < 1)
return;
var buffer = new byte[available];
sp.Read(buffer, 0, available);
var indata = BitConverter.ToString(buffer); // convert bytes to a hex representation
this.BeginInvoke(new SetTextDeleg(DisplayToUI), new object[] { indata });