I have two objects (of the same class) which each contain a SerialPort object. The class has a method which handles the SerialPort.DataReceived event and is used by both SerialPort objects.
When I instantiate each object in a separate application, each port handles its DataReceived event individually as expected.
When I instantiate two instances of the COM_Front_End class in the same application and send data from one serial port to the other, both port's DataReceived event handlers fire. For short, I'll call this "cross-talk".
My class structure looks something like this:
public class COM_Front_End
{
private SerialPort_custom port;
private LockObject;
public COM_Front_End(string PortName, string BaudRate)
{
// Other code
port = new SerialPort_custom(PortName, BaudRate, new SerialDataReceivedEventHandler(SerialDataReceived));
port.Open();
}
private void SerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
//lock (LockObject) // A lock is not needed here. Only one SerialDataReceived event can fire at a time
//{
SerialPort port;
try
{
port = sender as SerialPort;
if (port != null)
{
byte[] buffer = new byte[port.BytesToRead];
int bytesRead = port.Read(buffer, 0, buffer.Length);
foreach (byte inByte in buffer)
{
// Byte processing code
}
}
}
catch (Exception ex)
{
// Exception handling code
}
//}
}
}
The class containing the actual SerialPort class looks like:
public class SerialPort_custom : SerialPort
{
public SerialPort_custom(string PortName, int BaudRate, SerialDataReceivedEventHandler DataReceivedHandler)
{
this.PortName = PortName;
this.BaudRate = BaudRate;
this.Parity = System.IO.Ports.Parity.None;
this.DataBits = 8;
this.StopBits = System.IO.Ports.StopBits.One;
this.Handshake = System.IO.Ports.Handshake.None;
this.RtsEnable = true;
this.DtrEnable = true;
this.DiscardNull = false;
this.Encoding = Encoding.ASCII;
this.DataReceived += DataReceivedHandler;
}
// Other methods
}
I have two instances of the COM_Front_End class in the same application. Whenever one instance receives data, both objects' SerialDataReceived methods fire.
Why does the DataReceived event handler fire for both serial ports when they are instantiated in the same application? Furthermore, how can I ensure that multiple instantiation of this class does not cause "cross-talk"?
I've found the root cause of my problem:
The project in which the COM_Front_End resides has two static classes. One of these classes is the Receive Buffer and the other the Transmit Buffer. Changing these classes so that they are not static solved my problem. Within each COM_Front_End object is a task which polls the Receive Buffer. Since they both use the same static class, they both were pulling from this buffer which explains why
A. The SerialDataReceived for both objects fired.
B. The data received for each was mangled/partial.
TL;DR: Non-static objects containing static objects will yield shared resources whether it is intended or not.
Please correct me wherever my explanation is faulty or incomplete.
Related
After a long time i need to program again.
I need to constantly send a command through serial port from a car ecu(? data).
Then i need to receive that data which i will process to be shown on a display(thinking racing display with car parameters like temperature etc).
I need to do this constantly
I wonder before i start whats best way to do this?
1 thread for constantly asking and receiving data
main thread for showing data in screen.
(store data in buffer and save once a minute or so)
anyone has any tips a guide or so how to start on this.
i tested receiving data with terminal and i got data back so config is working.
sent ? data => i got data back.
You could just use the SerialPort class and configure the BaudRate, DataBits etc.. and then just wait for the DataReceived event to fire:
public class SerialPortReader
{
public SerialPortReader(string yourPortName)
{
var serialPort = new SerialPort() {
PortName = yourPortName,
BaudRate = 57600; //This will control the rate at what you receive the data
}
serialPort.DataReceived += new SerialDataReceivedEventHandler(OnDataReceived);
serialPort.Open();
}
}
public void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
var serialPort = (SerialPort)sender;
// Process your data reading the stream with Read, ReadLine etc...
}
The approach we are using in our SerialPort Handler is, to have an AutoResetEvent to get notified as soon as there is an answer from the port.
SerialPort class of the FrameWork has a few issues with the integrated DataReceived event. It is sometimes fired when there is no complete package available (in case you defined the answer length). So you should check for the answer length you expect.
Our very stripped down implementation:
public class Serialport
{
private SerialPort _serialPort;
private List<byte> _buffer;
private AutoResetEvent _autoResetEvent;
private const int WriteTimeOut = 5;
private event EventHandler ReceivedDataChanged;
public Serialport()
{
_serialPort = new SerialPort();
// set PortName, BaudRate etc
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer();
_serialPort.DataReceived += ReceiveData;
}
private void ReceiveData(object sender, SerialDataReceivedEventArgs e)
{
var bytes = _serialPort.BytesToRead;
byte[] buffer = new byte[bytes];
if (_serialPort.IsOpen)
{
_serialPort.BaseStream.Read(buffer, 0, bytes);
_buffer.AddRange(buffer);
}
ReceivedDataChanged?.Invoke(this, new ReceivedBytesEventArgs(_buffer.ToArray()));
_buffer.Clear();
}
private void SendData(byte[] message, int answerLength)
{
_serialPort.ReceivedBytesThreshold = answerLength;
_serialPort.WriteTimeout = WriteTimeOut;
_serialPort.Write(message, 0, message.Length);
}
public string SendDataCommand()
{
if (_serialPort.IsOpen)
{
ReceivedDataChanged += InterpretAnswer;
SendData(message, length);
if (_autoResetEvent.WaitOne(100))
{
ReceivedDataChanged -= InterpretAnswer;
//Data Received and interpreted and send to the caller
return _requestAnswer;
}
ReceivedDataChanged -= InterpretAnswer;
}
return "Connection not open";
}
private void InterpretAnswer(object sender, EventArgs e)
{
// handle all interpretation
// Set the event
_autoResetEvent.Set();
}
}
The serialPort is initialised and opened. After that, we wire up all needed events and call the SendDataCommand() Method. This method is the public visible method which is called from some task. This calls the method SendData. As soon as there is an answer, the event is triggered and the interpretation is started. If the interpretation is done in the specified amount of time (_autoResetEvent.WaitOne(msToWait)) the result is given back to the calling method.
This should be done in a separate task, so the ui will not Block while you wait for the answer
As mentioned, this is a very stripped down example. You should do more checking in the received handler of SerialPort, because there are some issues with the event. With this approach you will have a bit more of abstraction to your business logic.
Hope this helps.
I am very new to c#, and I'm creating a serial port class for a board I have designed. In which this class contains methods to open/close a serial port connected to the board. It should also read messages from the board and write messages from the UI to the board (I am using a forms application to input and display values).
I read the internal input buffer and place the bytes into my own software buffer, when a message is complete, this will prompt the form to analyse the message...
For this I have created an indexer to point to the array (from the form) and take the bytes that it desires.
uint[] serialPortReceiveBuffer = new uint[3];
public delegate void Del();
Del promptFormAction = Form1.MsgReceived;
public void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
for (int i = 0; i <= 2; i++)
{
serialPortReceiveBuffer[i] = (uint)serialPort1.ReadByte();
}
promptFormAction();
}
public uint this[uint i]
{
get { return serialPortReceiveBuffer[i]; }
}
this is the code within my pcbSerialPort class, and the code related to it in the Form1 class is as follows:
public static void MsgReceived()
{
Form1 _frm = new Form1();
_frm.analyzeIncomingMessage();
}
public void analyzeIncomingMessage()
{
if (PCB[0] == 63)
{
setBoardDesignator(PCB[1], PCB[2]);
}
}
My problem is that the when I use the indexer to access the serialPortReceiveBuffer, it doesn't see the changes that I made to it when placing received bytes into the same array. For example, when I receive a string of my own protocol --> "?10" the buffer is filled with [63][49][48]
Though when I try to access this buffer using the indexer I get [0][0][0]
Please can anyone help? Also, I'm aware there is probably a few other things I could have done better so if you have any general tips that would be great. Also in a language I may understand. I am just getting my head around many of the c# aspects, I have been doing embedded software for the past year but I wouldn't consider my self to be a competent programmer.
Thank you
From your code I'm not quite sure that the PCB object you're working with in your form is actually the one that receives the data. Might well be that you're working with two different instances, especially as you're creating a new instance of Form1 whenever data comes in!
(EDIT: From your comment to the question it is clear that this is exactly the problem. Follow these instructions to get closed to what you want).
I suggest that you redesign your code to pass the received message as an event to the existing form instance instead of how you do it now. Another problem you might run into will be that data you think you get will be overridden by the next message coming in, das the DataReceived event is asynchronous.
I'd declare an event that the form instance can subscribe to, passing the data to be analyzed into the event:
public class MessageReceivedEventArgs: EventArgs
{
public MessageReceivedEventArgs(byte[] data) : base()
{
Data = data;
}
public byte[] Data
{
get;
private set;
}
}
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
Then, I'd change your DataReceivedevent as follows:
public void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
for (int i = 0; i <= 2; i++)
{
serialPortReceiveBuffer[i] = (uint)serialPort1.ReadByte();
}
byte[] dataCopy = new byte[serialPortReceiveBuffer.Length];
Array.Copy(serialPortReceiveBuffer, dataCopy, dataCopy.Length);
promptFormAction(dataCopy);
}
private void promptForAction(byte[] data)
{
if (MessageReceived != null)
MessageReceived(this, new MessageReceivedEventArgs(data));
}
Also I'd keep the serialPortReceiveBuffer totally private to that class, as I said, you may run into synchronization issues if you don't. That'y why I copy the array before passing it to the event.
This change allows any subscriber to register for notifications whenever you realize that new data came in.
To use this, Form1 should look like this (roughly);
public class Form1
{
pcbSerialPort PCB; // The name of that class I don't know from your code
public Form1()
{
PCB = new pcbSerialPort();
PCB.MessageReceived += MessageReceived;
}
private void MessageReceived(object sender, pcbSerialPort.MessageReceivedEventArgs e)
{
analyzeIncomingMessage(e.Data);
}
private void analyzeIncomingMessage(byte[] data)
{
if (data[0] == 63)
{
setBoardDesignator(data[1], data[2]);
}
}
}
Another piece of advice on how you handle serial data: You need to decide whether you read from a serial port in a loop or whether you rely on the DataReceived event. Putting a loop into the event is not a good idea, as the event may be called upon arriving data again while you're waiting.
What you need to do is create a buffer that takes all the information from the serial port that's available. If you don't have enough data, don't wait for it. Instead add to the buffer whenever DataReceived is called and handle a message when enough data is present.
I think Thorsten's answer is good and it would make a lot of sense to redesign it along those lines, but as an absolute minimum, if you want to have it such that you create a new Form1 instance for every received message, then you will need to pass the instance of pcbSerialPort to MessageReceived and then to the constructor of your Form1 class. Something like:
Action<pcbSerialPort> promptFormAction = Form1.MsgReceived;
public void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// as Thorsten noted, you need to rethink this loop anyway
// what if there aren't at least three bytes to read?
for (int i = 0; i <= 2; i++)
{
serialPortReceiveBuffer[i] = (uint)serialPort1.ReadByte();
}
promptFormAction(this);
}
And your static method:
public static void MsgReceived(pcbSerialPort pcb)
{
Form1 _frm = new Form1(pcb);
_frm.analyzeIncomingMessage();
}
And you constructor for Form1:
public Form1(pcbSerialPort pcb)
{
PCB = pcb;
}
I'm writing a program that listens on a Serial Port. I already have code that utilizes the VCP drivers (Virtual COM Port) to open a serial connection and then add an event handler for any time data is received. That code roughly looks like this:
public void OpenPort(string portNumber)
{
_port = new SerialPort(
portName: portNumber,
baudRate: 9600,
parity: Parity.None,
dataBits: 8,
stopBits: StopBits.One
);
_port.DataReceived += ReadData;
}
private void ReadData(object sender, SerialDataReceivedEventArgs e)
{
string data = _port.ReadExisting().Trim();
Console.WriteLine("Received: " + data);
}
This works great. It's very easy for me to understand how to set up events using the += notation. But I'm trying to switch over from using the VCP drivers to instead using the D2XX drivers provided by FTDI. I have most of the equivalent code that I need written, with the notable exception of being able to read data whenever a "data received" event occurs.
The D2XX driver includes one method for setting up event handlers whenever data is received, called SetEventNotification. Here's what the method signature looks like:
SetEventNotification(UInt32 eventMask, EventWaitHandle eventHandle);
The first parameter is straight-forward enough (they have some predefined uints you can pass in to determine when the event should trigger), but I've never worked directly with EventWaitHandles before, and I found the documentation difficult to grasp, so I'm having trouble getting started.
At the end of the day... I would like to have an event listener method which performs a read task, and which I can assign using the += operator, as I did above with the VCP driver.
Based on what I was reading, it looks like I'll have to create a new Thread that essentially polls continuously for the EventWaitHandle's signal? Or something like that? Any examples or sample code to get me started (or finished!) would be appreciated.
Here's what I have so far:
public void OpenPort(string portNumber)
{
_port = new FTDI();
var status = _port.OpenBySerialNumber(portNumber);
if (FTDI.FT_STATUS.FT_OK != status) throw new Exception();
status = _port.SetBaudRate((UInt32) 9600);
if (FTDI.FT_STATUS.FT_OK != status) throw new Exception();
status = _port.SetDataCharacteristics(
DataBits: FTDI.FT_DATA_BITS.FT_BITS_8,
StopBits: FTDI.FT_STOP_BITS.FT_STOP_BITS_1,
Parity: FTDI.FT_PARITY.FT_PARITY_NONE
);
if (FTDI.FT_STATUS.FT_OK != status) throw new Exception();
var evHandle = new EventWaitHandle(false, EventResetMode.AutoReset, "");
_port.SetEventNotification(FTDI.FT_EVENTS.FT_EVENT_RXCHAR, evHandle);
// ... now what?
}
public void ReadData(object sender, EventArgs e)
{
UInt32 bytesAvailable = 0;
_port.GetRxBytesAvailable(ref bytesAvailable);
string data;
UInt32 bytesRead = 0;
_port.Read(out data, bytesAvailable, ref bytesRead);
data = data.Trim();
Console.WriteLine("Received: " + data);
}
I'll have to create a new Thread that essentially polls continuously for the EventWaitHandle's signal
Polls, no. But waits, yes. All that an event handle can do is let a thread sleep until the event is signaled. Note that "event" here means something completely different from a C# "event", though of course you can use the former as part of an implementation of the latter.
Frankly, it's not clear at all why you are headed down this part. Are you dealing with data transmitted over the standard serial port? If so, then there should never be any need to use some third-party API; Windows and .NET provide all you need, and you should stick with that. What does using this third-party API gain you that you are unable to accomplish using the standard SerialPort class?
As far as the event itself goes, without more context (and no, it's unlikely anyone would sift through the PDF you linked to figure out how to produce a turn-key solution for you), all one can offer is a general outline of how you can use the event handle to implement an event:
public event EventHandler DataReceived;
private bool _done;
private void PortListener(EventWaitHandle waitHandle)
{
while (true)
{
waitHandle.WaitOne();
if (_done)
{
break;
}
EventHandler handler = DataReceived;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
public void StartListening(EventWaitHandle waitHandle)
{
_done = false;
new Thread(() => PortListener(waitHandle)).Start()
}
public void StopListening(EventWaitHandle waitHandle)
{
_done = true;
waitHandle.Set();
}
The above provides a DataReceived C# event that is raised any time the wait handle is signaled. It assumes an auto-reset event. You can also use manual reset, simply by (of course) manually resetting the event handle any time it's signaled and you've raised the C# event.
To do this, it simply maintains an internal flag _done indicating whether the thread should be running or not, and provides the Start... and Stop... methods, which clear the flag and start the thread with its loop, and set the flag and signal the event, respectively.
I'm more familiar with C language and recently I've been ask to do C# for serial communication.
Below is my code for receiving data from COM port:
public void RxData()
{
int i = 0;
int Data;
bool StartRx = false;
int timer;
while (true)
{
Data = sp.ReadByte();
if (Data == 0x01)
{
StartRx = true;
}
if (StartRx == true)
{
RxBuffer[i++] = Data;
}
if (Data == 0x04)
{
RxChkSum = RxBuffer[i - 2];
break;
}
timer++;
if(timer>100)
{
timer = 0;
break;
}
}
}
Above is the way I receive data starting with 0x01 and ends with 0x04.
I'm incrementing a timer to count til 100 and quit the while loop in case I don't receive any data. Some sort like a timeout.
But seems like the timer don't work. When I don't receive any data, my program just stuck in the while loop forever.
I know this is the way we write in embedded c programming. But is this the right way to write in C#?
I think you might be interested in handling data coming from the serial port using an event handler. In the .net SerialPort class, you can register an event handler for data received:
var sp = new SerialPort("COM1") {
BaudRate = 9600,
Parity = Parity.None,
StopBits = StopBits.One,
DataBits = 8,
Handshake = Handshake.None
};
sp.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
You can also set the ReceivedBytesThreshold property on the SerialPort, which determines when your event handler will get fired.
Then, you just set up your event handler to read data and do what you need with it:
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string data = sp.ReadExisting();
}
Using this type of pattern, you don't have to loop, you just set the threshold you need, and let the framework call your event handler when the serial port's got that many bytes ready for you.
Hopefully that helps and I haven't missed your point completely. :)
ReadByte is a synchronous call. It will only return when there is a byte read.
To have your attempt getting to work you can check for available data before reading:
if(sp.BytesToRead > 0)
{
Data = sp.ReadByte();
}
Besides that I prefer asynchronous reading as hmqcnoesy suggested.
I've a serial port communication class which has the following code inside (among others but only the relevant part is shown):
public Form1 m_parent;
private delegate void ProcessPacketDelegate(byte[] packet);
public SerialPort comPort = new SerialPort();
//Constructor code
....setting baudrate, portname etc.
... setting m_parent as the main form
...setting other things
//add an event handler
comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
//constructor code end
void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int bytes = comPort.BytesToRead;
byte[] comBuffer = new byte[bytes];
comPort.Read(comBuffer, 0, bytes);
comPort.DiscardInBuffer();
m_parent.Invoke(new ProcessPacketDelegate(m_parent.ProcessPacket), comBuffer);
}
I'm instantiate this class from my main form, which address is stored in m_parent. The main form has a method called ProcessPacket, which processes the incoming packet. So far so good.
Now, I want to handle 2 serial ports so I need 2 instances of this class. However, I don't want them to use the same ProcessPacket method which could lead to packet collisions. I'd like to modify the serial port communication class in such way that in the constructor or by getsets I'd set callback method dynamically so instance #1 would invoke m_parentProcessPacket_A, instance #2 would invoke m_parentProcessPacket_B. Unfortunately this seems to be beyond my .NET skills, so any help would be great!
Thank you!
You can define a variable in constructor and use that.
if(condition)
handler = comPort_DataReceived;
else
handler = comPort_SomeElseMethod;
And then bind this
comPort.DataReceived += handler
Have a constructor like that:
YourClass(ProcessPacketDelegate process_packet)
{
// ...
this.process_packet = process_packet;
}
In your class, have also:
ProcessPacketDelegate process_packet;
Then in your comPort_DataReceived do:
m_parent.Invoke(this.process_packet, comBuffer);
In you main form when your instantiating your class do:
instance1 = new YourClass(new ProcessPacketDelegate(this.ProcessPacket_A));
instance2 = new YourClass(new ProcessPacketDelegate(this.ProcessPacket_B));