I have an application which connects with an external protocol
using serial communication.
I need know if the wakeup bit is set on each packet it sends to me (the 9 bit), and as communication rates must be below 40ms, and response must be sent under 20 ms.
The framework, encapsulates the bits read from the port, and only send back the 8 bits of data to me. Also, I cannot wait for the parity error event, because of timing issues.
I need to know how can I read the 9 bit, or if there is a free alternative to http://www.wcscnet.com/CdrvLBro.htm
Did you try to put your serial read function right in the parity error event handler? Depending on the driver, this might be fast enough.
This wouldn't happen to be for a certain slot machine protocol, would it? I did this for fun for you. Maybe it will work?
{
public Form1()
{
InitializeComponent();
}
SerialPort sp;
private void Form1_Load(object sender, EventArgs e)
{
sp = new SerialPort("COM1", 19200, Parity.Space, 8, StopBits.One);
sp.ParityReplace = 0;
sp.ErrorReceived += new SerialErrorReceivedEventHandler(sp_SerialErrorReceivedEventHandler);
sp.ReadTimeout = 5;
sp.ReadBufferSize = 256;
sp.Open();
}
object msgsLock = new object();
Queue<byte[]> msgs = new Queue<byte[]>();
public void sp_SerialErrorReceivedEventHandler(Object sender, SerialErrorReceivedEventArgs e)
{
if (e.EventType == SerialError.RXParity)
{
byte[] buffer = new byte[256];
try
{
int cnt = sp.Read(buffer, 0, 256);
byte[] msg = new byte[cnt];
Array.Copy(buffer, msg, cnt);
if (cnt > 0)
{
lock (msgsLock)
{
msgs.Enqueue(msg);
}
}
}
catch
{
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if (msgs.Count > 0)
{
lock (msgsLock)
{
listBox1.Items.Insert(0, BitConverter.ToString(msgs.Dequeue()));
}
}
}
}
}
Anyways, for more control over the serial port I suggest using the win32 calls to get what you want.
http://msdn.microsoft.com/en-us/magazine/cc301786.aspx
Related
I've been trying to get a live updating chart to work with WPF using livecharts, my goal is to have a chart update as it reads a serial input from an Arduino that just gives some numbers.
Using this example: https://lvcharts.net/App/examples/v1/wpf/Constant%20Changes
Although the example includes a built in randomizer of numbers, I want to switch that out for the Arduino serial input.
I read the serial input like this:
private void Button_Serial_Click(object sender, EventArgs e)
{
Thread thread = new Thread(SerialThread);
thread.Start();
}
static SerialPort _serialPort;
private void SerialThread() //serial thread starts here
{
_serialPort = new SerialPort();
_serialPort.PortName = "COM3";//Set your board COM
_serialPort.BaudRate = 9600;
try
{
_serialPort.Open();
}
catch (Exception e)
{
Console.WriteLine("could not connect to serial");
}
while (true)
{
string serialMSG = _serialPort.ReadExisting();
Console.WriteLine(serialMSG);
Thread.Sleep(200);
}
}
My problem is that I don't know what code to switch out for it to read the serial instead of the built in randomizer the example uses. The example has no usable comments or explanation of how it works and my inexperience with coding makes me unable to understand it fully.
I've looked at similar issues, but most just say to read through livechart examples. Well I did, but I do not understand it enough still.
Any assistance is appreciated.
Instead of while(true), you should let c# you decided when data
class Program
{
private static SerialPort port = new SerialPort("COM3",
9600, Parity.None, 8, StopBits.One);
private static ChartValues<MeasureModel> _chartValues;
static void Main(string[] args)
{
SerialPortWorker();
Console.Read();
}
private static void SerialPortWorker()
{
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); //called when the data waiting in the buffer
port.Open(); //// Begin communications
Console.ReadLine(); // keep console thread alive
}
private static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if(port.BytesToWrite > 0)
{
var data = port.ReadExisting(); //read incoming data
_chartValues.Add(new MeasureModel
{
DateTime = DateTime.Now,
Value = data
});
SetAxisLimits(now);
//lets only use the last 150 values
if (ChartValues.Count > 150) ChartValues.RemoveAt(0);
}
}
}
class MeasureModel
{
public DateTime DateTime { get; set; }
public String Value { get; set; }
}
I currently have a program made using VB6 code that uses the MSCOMM control to pull back data from the serial port. This manages to successfully receive the data from my serial port, in which a Denso BHT-904B device is connected.
I am now trying to move this code over to C# so it fits in with a new piece of software that i am developing. To do this i am using the SerialPort class. However, the issue is that when i open the port up the data received event only fires when the device fails to communicate (which im guessing is due to a timeout). The data then received in the event is '↑↑↑↑↑'.
My SerialPort control settings are the following:
DtrEnable = True
PortName = COM3
ReadBufferSize = 1024
WriteBufferSize = 512
The code that i am using behind my form control is:
namespace BHTTestingDotNet
{
public partial class Form1 : Form
{
private string rxString;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
serialPort.DtrEnable = true;
serialPort.Encoding = Encoding.Default;
serialPort.DataReceived += serialPort_DataReceived;
serialPort.ErrorReceived += serialPort_ErrorReceived;
serialPort.Open();
}
private void serialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
MessageBox.Show(e.ToString());
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
var serialPort = (SerialPort)sender;
var test = serialPort.BytesToRead;
SerialPort sr = (SerialPort)sender;
rxString = sr.ReadExisting();
this.BeginInvoke(new EventHandler(displayText));
}
private void displayText(object o, EventArgs e)
{
txtBHT.AppendText(rxString);
}
}
}
I have already tried to set both RtsEnable and DtrEnable to true but that didn't make any difference.
UPDATE - I have now changed to protocol settings on the device but i now only receive pipes and then a return symbol, for example like so:
|||||¬
I am using SerialPort class often and for my purposes I have made my own class
public class SerialPortDataSource : SerialPort
where SerialPort.DataReceived handler invoke this method:
private void SerialPortDataSource_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
if (BytesToRead > 0)
{
var buffor = new byte[BytesToRead];
Read(buffor, 0, buffor.Length);
_receivedBytes = buffor;
//wConsole.WriteLine(ArrayExtension.ToString(buffor));
var dataLogger = DataLogger;
if (dataLogger != null)
{
dataLogger.WriteLine("- DR - {0}", true, BitConverterExtension.ToHexString(buffor));
}
if (OnDataReceived != null)
{
OnDataReceived(this, buffor);
}
}
}
catch (InvalidOperationException)
{
// sometimes DataReceived event is invoked after port is closed which causes InvalidOperationException
}
}
This method is working for me in many applications with variety serial port settings.
i have a serial port that will iterate through the ports with this method:
foreach (string s in SerialPort.GetPortNames())
{
var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
if (serialOneOfMany.IsOpen)
{
serialOneOfMany.Close();
}
else
{
try
{
serialOneOfMany.Open();
}
catch
{
var openSerial = new System.Timers.Timer(3100);
openSerial.Elapsed += (o, e) =>
{
serialOneOfMany.Open();
openSerial.Enabled = false;
openSerial.Dispose();
};
openSerial.Enabled = true;
}
}
if (serialOneOfMany.IsOpen)
{
string received;
try
{
lblPortNum.Content = s;
lblPortNum.Refresh();
serialOneOfMany.Write(testMessage);
serialOneOfMany.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);
}
catch (TimeoutException e)
{
serialOneOfMany.Close();
continue;
}
}
}
so, i want to open the port, send it a message, listen for the response, then close it. as everyone knows, every comport found in GetPortNames isn't a valid serial port. so, what i've been doing is setting a timer with a dispatcher timer:
DispatcherTimer time = new DispatcherTimer();
time.Interval = TimeSpan.FromMilliseconds(3000);
time.Tick += new EventHandler(someEventHandler);
time.Start();
here's the other method handled here:
private void someEventHandler(Object sender, EventArgs args)
{
SerialPort serial = (SerialPort)sender;
if (serial.IsOpen)
serial.Close();
serial.Dispose();
//if you want this event handler executed for just once
DispatcherTimer thisTimer = (DispatcherTimer)sender;
thisTimer.Stop();
}
so, it'll open the com port, if it doesn't get a response within 3 seconds, it will close the port. the problem i'm having is that the foreach loop will just barrel through the code and open the comport several times, i'll get a message saying The COM Port is open already and can't be used. so basically it's not pausing in openSerial.
i want it to open a new serial port, and if it's not accessible, wait 3100 milliseconds and try again. how do i do that?
UPDATED CODE:
private void button1_Click(object sender, RoutedEventArgs e)
{
CheckPorts();
}
private void checkPorts()
{
SendMessage("messageToDevice1", 19200);
SendMessage("Message2", 9600);
}
private void SendMessage(string testMessage, int baudRate)
{
int baudRate = 9600;
string testMessage = "test";
txtPortName.Text = "Testing all serial ports";
foreach (string s in SerialPort.GetPortNames())
{
SerialPort newPort = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
if (!newPort.IsOpen)
{
try
{
newPort.Open();
}
catch { }
}
if (newPort.IsOpen)
{
openPorts.Add(newPort);
newPort.Write(testMessage);
newPort.DataReceived += new SerialDataReceivedEventHandler(serialOneOfMany_DataReceived);
}
else
{
newPort.Dispose();
}
}
txtPortName.Text = "Waiting for response";
tmrPortTest.Enabled = true;
}
my new problem is that it just blows through the com ports, i need it to stop for each one, take a second to listen, then close it. it just blows through the foreach loop.
now, the reason why i don't just open up the port and keep it open through all the messages is that my devices have different baud rates, and i can't adjust them to all match. so, i need to open the ports, then send messages, listen, if they don't respond to the first round of messages, then open them up at the new baudrate and send a new batch of messages. but the foreachloop doens't pause for me to listen.
I think this more or less agrees with rare's answer. The port where you receive a response (you would probably want to check the response as well) will remain open and all the others should close.
private List<SerialPort> openPorts = new List<SerialPort>();
private void button3_Click(object sender, EventArgs e)
{
int baudRate = 9600;
string testMessage = "test";
txtPortName.Text = "Testing all serial ports";
foreach (string s in SerialPort.GetPortNames())
{
SerialPort newPort = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
if (!newPort.IsOpen)
{
try
{
newPort.Open();
}
catch { }
}
if (newPort.IsOpen)
{
openPorts.Add(newPort);
newPort.Write(testMessage);
newPort.DataReceived += new SerialDataReceivedEventHandler(serialOneOfMany_DataReceived);
}
else
{
newPort.Dispose();
}
}
txtPortName.Text = "Waiting for response";
tmrPortTest.Enabled = true;
}
private void serialOneOfMany_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
txtPortName.Text = ((SerialPort)sender).PortName;
}
private void tmrPortTest_Tick(object sender, EventArgs e)
{
tmrPortTest.Enabled = false;
foreach (SerialPort port in openPorts)
{
if (port.PortName != txtPortName.Text)
{
port.Close();
port.Dispose();
}
}
}
Here's how I would do this --
First, try to open all the serial ports. The ones that actually do open are put in a list.
Assign all serial ports in the list to the same DataReceived event handler. The event handler is where you will save the port name (it's in the args) and kill the timer if you rx'd the response
Send your testMessage out all the open ports
Set just one timer for 3.1 seconds
Close the ports once the timer fires or the event handler rx's the response.
i have a problem
i am writing a code in C#
i wanna receive a byte from serial port
but when i wanna receive data from port that sounds my program is hang
and doesnt work any more
SerialPort port = new SerialPort("COM3");
port.Open();
byte[] b = new byte[10];
port.Read(b, 0, 1);
port.Close();
please help me
This is because SerialPort reads data synchronously and blocks current thread until the data would be available.
You can use separate thread for this:
public class SerialPort : IDisposable
{
public SerialPort(byte comNum, int baudRate)
{
this.comNum = comNum;
serialPort = new System.IO.Ports.SerialPort("COM" + comNum.ToString(), baudRate);
serialPort.Open();
thread = new System.Threading.Thread(ThreadFn);
thread.Start();
}
public void Dispose()
{
if (thread != null)
thread.Abort();
if (serialPort != null)
serialPort.Dispose();
}
private void OnReceiveByte(byte b)
{
//handle received byte
}
private void ThreadFn(object obj)
{
Byte[] inputBuffer = new Byte[inputBufferSize];
while (true)
{
try
{
int availibleBytes = serialPort.BytesToRead;
if (availibleBytes > 0)
{
int bytesToRead = availibleBytes < inputBufferSize ? availibleBytes : inputBufferSize;
int readedBytes = serialPort.Read(inputBuffer, 0, bytesToRead);
for (int i = 0; i < readedBytes; i++)
OnReceiveByte(inputBuffer[i]);
}
System.Threading.Thread.Sleep(1);
}
catch (System.Threading.ThreadAbortException)
{
break;
}
catch (Exception e)
{
System.Diagnostics.Debug.Assert(false, e.Message);
}
}
}
private Byte comNum;
private System.IO.Ports.SerialPort serialPort;
private System.Threading.Thread thread;
private const int inputBufferSize = 1024;
}
Is there actually any data being sent over the serial port? The call to Read might just be waiting to receive some data before returning. Make sure that you have set a value for the ReadTimeout property. This will make the call to Read throw a TimeoutException if no data was read from the port.
Reference:
http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.readtimeout.aspx
Also make sure you set the serial speed right (if you're reading too fast you'll miss some data, etc)
I have some SerialPort code that constantly needs to read data from a serial interface (for example COM1). But this seems to be very CPU intensive and if the user moves the window or a lot of data is being displayed to the window (such as the bytes that are received over the serial line) then communication gets messed up.
Considering the following code:
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[port.ReadBufferSize];
var count = 0;
try
{
count = port.Read(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
if (count == 0)
return;
//Pass the data to the IDataCollector, if response != null an entire frame has been received
var response = collector.Collect(buffer.GetSubByteArray(0, count));
if (response != null)
{
this.OnDataReceived(response);
}
The code needs to be collected as the stream of data is constant
and the data has to be analyzed for (frames/packets).
port = new SerialPort();
//Port configuration code here...
this.collector = dataCollector;
//Event handlers
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
port.Open();
If there is no user interaction and nothing being added to the window,
this works fine but as soon as there is interaction communication really gets messed up.
Timeouts occur etc....
For example, this messes everything up:
Dispatcher.BeginInvoke(new Action(() =>
{
var builder = new StringBuilder();
foreach (var r in data)
{
builder.AppendFormat("0x{0:X} ", r);
}
builder.Append("\n\n");
txtHexDump.AppendText(builder.ToString());
txtHexDump.ScrollToEnd();
}),System.Windows.Threading.DispatcherPriority.ContextIdle);
});
But even simple calls to log4net cause problems.
Are there any best practices to optimize SerialPort communication
or can someone tell me what I'm doing wrong...
Update:
In case the above didn't make much sence. I made a very simple (and stupid) little example:
class Program
{
static void Main(string[] args)
{
var server = new BackgroundWorker();
server.DoWork += new DoWorkEventHandler(server_DoWork);
server.RunWorkerAsync();
var port = new SerialPort();
port.PortName = "COM2";
port.Open();
string input = "";
Console.WriteLine("Client on COM2: {0}", Thread.CurrentThread.ManagedThreadId);
while (input != "/quit")
{
input = Console.ReadLine();
if (input != "/quit")
{
var data = ASCIIEncoding.ASCII.GetBytes(input);
port.Write(data, 0, data.Length);
}
}
port.Close();
port.Dispose();
}
static void server_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Listening on COM1: {0}", Thread.CurrentThread.ManagedThreadId);
var port = new SerialPort();
port.PortName = "COM1";
port.Open();
port.ReceivedBytesThreshold = 15;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var port = (SerialPort)sender;
int count = 0;
byte[] buffer = new byte[port.ReadBufferSize];
count = ((SerialPort)sender).Read(buffer, 0, buffer.Length);
string echo = ASCIIEncoding.ASCII.GetString(buffer,0,count);
Console.WriteLine("-->{1} {0}", echo, Thread.CurrentThread.ManagedThreadId);
}
}
The result might look like this:
Listening on COM1: 6
Client on COM2: 10
This is some sample data that I send
---> 6 This is some sample data that I send
So reading the data from the port happens on the main thread....
Might this be part of what's causing my problems?
I am surprised no one caught this. The SerialPort class utilizes its own thread when using the DataReceived event. This means that if the subscriber, for example, is accessing any Form elements, then it must be done so with either an Invoke, or BeginInvoke method(s). Otherwise, you end up with a cross thread operation. In older versions of .net, this would go along unnoticed with unpredictable behaviour (depending on the CPU cores in the PC) and in later versions, should raise an exception.
Your last conclusion, that the event runs on the Main thread, may not be true for Windows App. Don't test this in a Console.
The proper way to tune this is:
set a large enough buffer, although the minimum 4096 is usually Ok
set the ReceivedBytesThreshold as high as tolerable (and do it before the Open())
do as little as possible in the Received event, pass the
data to a Queue or MemoryStream if you need more time
You should rewrite port_DataReceived procedure to read data until port.BytesToRead is greater then zero, like this:
private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
var port = (SerialPort)sender;
while (port.BytesToRead > 0)
{
int byte_count = port.BytesToRead;
byte[] buffer = new byte[byte_count];
int read_count = port.Read(buffer, 0, byte_count);
// PROCESS DATA HERE
}
}
Also I would recommend you to just insert data in queue list in procedure port_DataReceived, and perform data processing in separate thread.
The classic solution is to have a FIFO buffer. Ensure that the size of the FIFO is large enough to handle any critical case where there is a lot of input and the processor block is being taken up.
You could even have a 2-buffer system:
--->|Reader|-->FIFO-->|Processor|--->FIFO2--->|Displayer|