I need to continuously read a serial port, and log the data to a file, using a timer. The data is coming in fast, 4800 baud rate and I need to log the most recently read data. I am using the DataReceivedEventHandler and the ReadLine() method. The bool below, okToReadPort is set to false when the timer event occurs to write to the file. Basic code flow is below
...
serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
...
//serial reader class
private void DataReceivedHandler(object sender,SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
if (okToReadPort)
{
try
{
string indata = sp.ReadLine();
Console.Write("NMEA Sentence read: " + indata);
NMEAMsg tempMsg = new NMEAMsg(indata);
int count = nmeaList.Count;
for (int i = 0; i < count; i++)
{
if (tempMsg.sentenceType == nmeaList[i].sentenceType)
{
nmeaList[i] = tempMsg;
}
}
}
else
//do nothing
...
//form class
...
timerEventOccurred(){
serialReader.okToReadPort = false;
writeDataToFile(serialReader.data);
}
I am getting an error while trying to enumerate the data list "Collection was modified; enumeration operation may not execute" it reads and writes data fine, but it seems like there are some threads that do not finish and are still reading the port when the timer event has occurred.
Does anyone have a better way to accomplish this?
I have a Dealer <--> Router setup in NetMQ v4 where I can asynchronously send and receive messages in any direction with no problems.
I now want to formalize that into an abstraction where the server (Router) listens for any incoming message but it also need to on demand broadcasts messages to any of the connected clients (Dealers).
I am trying to avoid using Pub <--> Sub sockets as I need the subscribers to also send messages to the server. The closest pattern to what I am trying to achieve is a WebSocket client-server communication.
The first part of listening to the client messages are done in something like:
using (var server = new RouterSocket("#tcp://*:80"))
{
var addresses = new HashSet<string>();
while (true)
{
var msg = server.ReceiveMultipartMessage();
var address = Encoding.UTF8.GetString(msg[0].Buffer);
var payload = Encoding.UTF8.GetString(msg[2].Buffer);
Console.WriteLine("[Server] - Client: {0} Says: {1}", address, payload);
var contains = addresses.Contains(address);
if (!contains) { addresses.Add(address); }
msg.Clear();
msg.Append(address);
msg.AppendEmptyFrame();
msg.Append("Reply for: " + address);
server.SendMultipartMessage(msg);
}
}
Now given that the sockets are not Thread-Safe, I am stuck on finding a way to broadcasts messages (coming from a different thread on demand) to all the clients.
I can probably use the TryReceiveMultipartMessage method in the loop instead with a set timeout after which I can check a queue for any broadcast messages and then loop through each client sending such message. Something like:
using (var server = new RouterSocket("#tcp://*:80"))
{
var addresses = new HashSet<string>();
var msg = new NetMQMessage();
while (true)
{
var clientHasMsg = server.TryReceiveMultipartMessage(TimeSpan.FromSeconds(1), ref msg);
if (!clientHasMsg)
{
// Check any incoming broacast then loop through all the clients
// sending each the brodcast msg
var broadMsg = new NetMQMessage();
foreach (var item in addresses)
{
broadMsg.Append(item);
broadMsg.AppendEmptyFrame();
broadMsg.Append("This is a broadcast");
server.SendMultipartMessage(broadMsg);
broadMsg.Clear();
}
// Go back into the loop waiting for client messages
continue;
}
var address = Encoding.UTF8.GetString(msg[0].Buffer);
var payload = Encoding.UTF8.GetString(msg[2].Buffer);
Console.WriteLine("[Server] - Client: {0} Says: {1}", address, payload);
var contains = addresses.Contains(address);
if (!contains) { addresses.Add(address); }
msg.Clear();
msg.Append(address);
msg.AppendEmptyFrame();
msg.Append("Reply for: " + address);
server.SendMultipartMessage(msg);
}
}
This somehow does not feel right mainly due to:
What value for the timeout is a good value? 1 sec, 100 ms etc;
Is this the most efficient/performing solution as this program will be used to have 100k+ clients connected with each sending thousands of messages per second.
Any pointers on what's the best approach to this is very much appreciated.
You can use netmqqueue, it multi producer single consumer queue. You can add it to NetMQPoller and enqueue from multiple threads without lock.
I think PUB/SUB is a proper approach for your requirements of 100k+ clients. Nevertheless, it doesn't mean that you can't communicate back to server: use DEALER/ROUTER. Why do you think this solution is not acceptable?
I have a .NET 3.5 client/server socket interface using the asynchronous methods. The client connects to the server and the connection should remain open until the app terminates. The protocol consists of the following pattern:
send stx
receive ack
send data1
receive ack
send data2 (repeat 5-6 while more data)
receive ack
send etx
So a single transaction with two datablocks as above would consist of 4 sends from the client. After sending etx the client simply waits for more data to send out, then begins the next transmission with stx. I do not want to break the connection between individual exchanges or after each stx/data/etx payload.
Right now, after connection, the client can send the first stx, and get a single ack, but I can't put more data onto the wire after that. Neither side disconnects, the socket is still intact. The client code is seriously abbreviated as follows - I'm following the pattern commonly available in online code samples.
private void SendReceive(string data) {
// ...
SocketAsyncEventArgs completeArgs;
completeArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend);
clientSocket.SendAsync(completeArgs);
// two AutoResetEvents, one for send, one for receive
if ( !AutoResetEvent.WaitAll(autoSendReceiveEvents , -1) )
Log("failed");
else
Log("success");
// ...
}
private void OnSend( object sender , SocketAsyncEventArgs e ) {
// ...
Socket s = e.UserToken as Socket;
byte[] receiveBuffer = new byte[ 4096 ];
e.SetBuffer(receiveBuffer , 0 , receiveBuffer.Length);
e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive);
s.ReceiveAsync(e);
// ...
}
private void OnReceive( object sender , SocketAsyncEventArgs e ) {}
// ...
if ( e.BytesTransferred > 0 ) {
Int32 bytesTransferred = e.BytesTransferred;
String received = Encoding.ASCII.GetString(e.Buffer , e.Offset , bytesTransferred);
dataReceived += received;
}
autoSendReceiveEvents[ SendOperation ].Set(); // could be moved elsewhere
autoSendReceiveEvents[ ReceiveOperation ].Set(); // releases mutexes
}
The code on the server is very similar except that it receives first and then sends a response - the server is not doing anything (that I can tell) to modify the connection after it sends a response. The problem is that the second time I hit SendReceive in the client, the connection is already in a weird state.
Do I need to do something in the client to preserve the SocketAsyncEventArgs, and re-use the same object for the lifetime of the socket/connection? I'm not sure which eventargs object should hang around during the life of the connection or a given exchange.
Do I need to do something, or Not do something in the server to ensure it continues to Receive data?
The server setup and response processing looks like this:
void Start() {
// ...
listenSocket.Bind(...);
listenSocket.Listen(0);
StartAccept(null); // note accept as soon as we start. OK?
mutex.WaitOne();
}
void StartAccept(SocketAsyncEventArgs acceptEventArg) {
if ( acceptEventArg == null )
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
}
Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg);
if ( !willRaiseEvent )
ProcessAccept(acceptEventArg);
// ...
}
private void OnAcceptCompleted( object sender , SocketAsyncEventArgs e ) {
ProcessAccept(e);
}
private void ProcessAccept( SocketAsyncEventArgs e ) {
// ...
SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs();
readEventArgs.SetBuffer(dataBuffer , 0 , Int16.MaxValue);
readEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnIOCompleted);
readEventArgs.UserToken = e.AcceptSocket;
dataReceived = ""; // note server is degraded for single client/thread use
// As soon as the client is connected, post a receive to the connection.
Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
if ( !willRaiseEvent )
this.ProcessReceive(readEventArgs);
// Accept the next connection request.
this.StartAccept(e);
}
private void OnIOCompleted( object sender , SocketAsyncEventArgs e ) {
// switch ( e.LastOperation )
case SocketAsyncOperation.Receive:
ProcessReceive(e); // similar to client code
// operate on dataReceived here
case SocketAsyncOperation.Send:
ProcessSend(e); // similar to client code
}
// execute this when a data has been processed into a response (ack, etc)
private SendResponseToClient(string response) {
// create buffer with response
// currentEventArgs has class scope and is re-used
currentEventArgs.SetBuffer(sendBuffer , 0 , sendBuffer.Length);
Boolean willRaiseEvent = currentClient.SendAsync(currentEventArgs);
if ( !willRaiseEvent )
ProcessSend(currentEventArgs);
}
A .NET trace shows the following when sending ABC\r\n:
Socket#7588182::SendAsync()
Socket#7588182::SendAsync(True#1)
Data from Socket#7588182::FinishOperation(SendAsync)
00000000 : 41 42 43 0D 0A
Socket#7588182::ReceiveAsync()
Exiting Socket#7588182::ReceiveAsync() -> True#1
And it stops there. It looks just like the first send from the client but the server shows no activity.
I think that could be info overload for now but I'll be happy to provide more details as required.
Thanks!
Firstly, I'm sure you know, but it's worth repeating; a TCP connection is a stream of bytes. Each read can return between 1 byte and the size of the buffer that you used depending on the amount of data that has arrived. Just because you send the data with 3 sends doesn't mean that you'll need 3 recvs to read it, you could get it all in one, or each recv could return a single byte. It's up to YOU to deal with message framing as you're the only one that knows about it. TCP is a stream of bytes.
Also WHY are you using those events? If you don't want to use asynchronous data flow then don't use the async functions, write something very simple with the sync socket functions and remove all the pointless complexity of using an async API and then using synchronisation primitives to hobble it.
I am trying to read data from an RS-232 port. Does anyone have an example of how I get the data from the port/buffer and make sure that I have all the data as it can be multiline data.
Do I simply read it as follows ?
string Rxstring = port.ReadLine();
Console.WriteLine(Rxstring);
Q: how to get the date from the port/buffer, or input data from your connected device. AND make sure that you have all the data.
A: i have worked extensively with .net serial port class drivers where i was tasked to create reliable, robust code. this means that a connected device under test has to run and NOT fail over a LONG period of time. Serial port can AND does lose data! don't forget that.
//from top of the head;
using System.Port.IO;
using System.Port;
private class mywindowsForm: Form
{
StringBuilder sbReceived = new StringBuilder();
string Received = string.Empty;
int byteCOUNT = 0;
System.Windows.Timers.Timer serialTimer;
//Default constructor
myWindowsForm()
{
//assume that you clicked and dragged serial port in
serialPort1 = new SerialPort();//create new serial port instance
serialPort1.Baud = 9600;
serialPort1.DataReceived+=<Tab><Enter>
//serial port timer
serialTimer = new System.Windows.Timers.Timer(500);//set to 500ms time delay
serialTimer.Elapsed+=<TAB><ENTER>
}
void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//serial port has detected input data
//however, we only want to get serial data so,
if(e.EventType !=SerialData.Chars) return;
//good design practice dictates that we have an event handler that we invoke
this.BeginInvoke(new eventhandler(AddReceive));//beginInvoke is designed to deal with asynchronous processes like serial port data.
}
private void AddReceive(object s, EventArg e)
{
byteCOUNT=serialPort1.BytesToRead;//count the number of bytes in RX buffer
if(byteCOUNT > 0)
{
string ST = serialPort1.ReadTo("\n");//lets get one line at a time
sbReceived.Append(ST);//add whatever has been RX'd to our container.
serialPort1.Interval =100;
serialPort1.Start();//to be sure we have all data, check to see for stragglers.
}
}
void serialTimer(object Sender, TimerElapsedEventArgs e)
{
serialTimer.Stop();
this.BeginInvoke(new EventHandler(ReadData));
}
void ReadData(object Sender, EventArgs e)
{
//parse output for required data and output to terminal display (build one using rich text box)
Received = sbReceived.ToString();
//and if we have ANY MORE incoming data left over in serial buffer
if(Received.Length > 0)
{
//your data
}
}
}
this should be plenty to get you started. this is result of years of creating customized terminal emulators in c#. there are other things that can be done, particularly if you have large amount of i/o data you need to set up handshaking with device. you have to let the device handle at a rate that the device is happy with. in cases where larger data has to be transferred consider setting up a simple packet passing protocol and command semaphore construct - or use a protocol as defined that the controller / device is designed to work with.
Try this:
using System.IO.Ports;
...
private SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
Console.WriteLine(port.ReadExisting());
Details can be found at Coad's Code.
I am attempting to create a small application to collect data received from an external sensor attached to COM10. I have successfully created a small C# console object and application that opens the port and streams data to a file for a fixed period of time using a for-loop.
I would like to convert this application to use the dataReceived event to stream instead. After reading the Top 5 SerialPort Tips, I still can't seem to get this to work and don't know what I am missing. I rewrote the console application so that all the code is in Main and is pasted below. Can someone please help enlighten me as to why the event handler port_OnReceiveDatazz is not being called even though I know that there is data being sent to the port by the hardware?
Thanks
Thanks to #Gabe, #Jason Down, and #abatishchev for all the suggestions. I am stumped and can't seem to get the event handler to work. Perhaps it has something to do with the device. I can live with just reading the port in a thread and streaming the data straight to file.
Code
namespace serialPortCollection
{ class Program
{
static void Main(string[] args)
{
const int bufSize = 2048;
Byte[] buf = new Byte[bufSize]; //To store the received data.
SerialPort sp = new SerialPort("COM10", 115200);
sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler
sp.Open();
sp.WriteLine("$"); //Command to start Data Stream
// Wait for data or user input to continue.
Console.ReadLine();
sp.WriteLine("!"); //Stop Data Stream Command
sp.Close();
}
// My Event Handler Method
private static void port_OnReceiveDatazz(object sender,
SerialDataReceivedEventArgs e)
{
SerialPort spL = (SerialPort) sender;
const int bufSize = 12;
Byte[] buf = new Byte[bufSize];
Console.WriteLine("DATA RECEIVED!");
Console.WriteLine(spL.Read(buf, 0, bufSize));
}
}
}
I think your issue is the line:**
sp.DataReceived += port_OnReceiveDatazz;
Shouldn't it be:
sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);
**Nevermind, the syntax is fine (didn't realize the shortcut at the time I originally answered this question).
I've also seen suggestions that you should turn the following options on for your serial port:
sp.DtrEnable = true; // Data-terminal-ready
sp.RtsEnable = true; // Request-to-send
You may also have to set the handshake to RequestToSend (via the handshake enumeration).
UPDATE:
Found a suggestion that says you should open your port first, then assign the event handler. Maybe it's a bug?
So instead of this:
sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);
sp.Open();
Do this:
sp.Open();
sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);
Let me know how that goes.
First off I recommend you use the following constructor instead of the one you currently use:
new SerialPort("COM10", 115200, Parity.None, 8, StopBits.One);
Next, you really should remove this code:
// Wait 10 Seconds for data...
for (int i = 0; i < 1000; i++)
{
Thread.Sleep(10);
Console.WriteLine(sp.Read(buf,0,bufSize)); //prints data directly to the Console
}
And instead just loop until the user presses a key or something, like so:
namespace serialPortCollection
{ class Program
{
static void Main(string[] args)
{
SerialPort sp = new SerialPort("COM10", 115200);
sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler
sp.Open();
sp.WriteLine("$"); //Command to start Data Stream
Console.ReadLine();
sp.WriteLine("!"); //Stop Data Stream Command
sp.Close();
}
// My Event Handler Method
private static void port_OnReceiveDatazz(object sender,
SerialDataReceivedEventArgs e)
{
SerialPort spL = (SerialPort) sender;
byte[] buf = new byte[spL.BytesToRead];
Console.WriteLine("DATA RECEIVED!");
spL.Read(buf, 0, buf.Length);
foreach (Byte b in buf)
{
Console.Write(b.ToString());
}
Console.WriteLine();
}
}
}
Also, note the revisions to the data received event handler, it should actually print the buffer now.
UPDATE 1
I just ran the following code successfully on my machine (using a null modem cable between COM33 and COM34)
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Thread writeThread = new Thread(new ThreadStart(WriteThread));
SerialPort sp = new SerialPort("COM33", 115200, Parity.None, 8, StopBits.One);
sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler
sp.Open();
sp.WriteLine("$"); //Command to start Data Stream
writeThread.Start();
Console.ReadLine();
sp.WriteLine("!"); //Stop Data Stream Command
sp.Close();
}
private static void port_OnReceiveDatazz(object sender,
SerialDataReceivedEventArgs e)
{
SerialPort spL = (SerialPort) sender;
byte[] buf = new byte[spL.BytesToRead];
Console.WriteLine("DATA RECEIVED!");
spL.Read(buf, 0, buf.Length);
foreach (Byte b in buf)
{
Console.Write(b.ToString() + " ");
}
Console.WriteLine();
}
private static void WriteThread()
{
SerialPort sp2 = new SerialPort("COM34", 115200, Parity.None, 8, StopBits.One);
sp2.Open();
byte[] buf = new byte[100];
for (byte i = 0; i < 100; i++)
{
buf[i] = i;
}
sp2.Write(buf, 0, buf.Length);
sp2.Close();
}
}
}
UPDATE 2
Given all of the traffic on this question recently. I'm beginning to suspect that either your serial port is not configured properly, or that the device is not responding.
I highly recommend you attempt to communicate with the device using some other means (I use hyperterminal frequently). You can then play around with all of these settings (bitrate, parity, data bits, stop bits, flow control) until you find the set that works. The documentation for the device should also specify these settings. Once I figured those out, I would make sure my .NET SerialPort is configured properly to use those settings.
Some tips on configuring the serial port:
Note that when I said you should use the following constructor, I meant that use that function, not necessarily those parameters! You should fill in the parameters for your device, the settings below are common, but may be different for your device.
new SerialPort("COM10", 115200, Parity.None, 8, StopBits.One);
It is also important that you setup the .NET SerialPort to use the same flow control as your device (as other people have stated earlier). You can find more info here:
http://www.lammertbies.nl/comm/info/RS-232_flow_control.html
By the way, you can use next code in you event handler:
switch(e.EventType)
{
case SerialData.Chars:
{
// means you receives something
break;
}
case SerialData.Eof:
{
// means receiving ended
break;
}
}
I was having the very same problem with a modem that had previously worked and then one day just stopped raising the DataReceived event.
The solution in my case, very randomly, was to enable RTS e.g.
sp.RtsEnable = true;
No idea why that worked on this particular bit of kit (not a comms man at all really), nor why it had worked and then stopped but it may help somebody else one day so just posting it just in case...
Might very well be the Console.ReadLine blocking your callback's Console.Writeline, in fact. The sample on MSDN looks ALMOST identical, except they use ReadKey (which doesn't lock the console).
I believe this won't work because you are using a console application and there is no Event Loop running. An Event Loop / Message Pump used for event handling is setup automatically when a Winforms application is created, but not for a console app.
Be aware that there are problems using .NET/C# and any COM port higher than COM9.
See: HOWTO: Specify Serial Ports Larger than COM9
There is a workaround in the format: "\\.\COM10" that is supported in the underlying CreateFile method, but .NET prevents using that workaround format; neither the SerialPort constructor nor the PortName property will allow a port name that begins with "\"
I've been struggling to get reliable communications to COM10 in C#/.NET. As an example, if I have a device on COM9 and COM10, traffic intended for COM10 goes to the device on COM9! If I remove the device on COM9, COM10 traffic goes to the device on COM10.
I still haven't figured how to use the handle returned by CreateFile to create a C#/.NET style SerialPort object, if I knew how to do that, then I think I could use COM10+ just fine from C#.
switch(e.EventType)
{
case SerilData.Chrs:
{
// means you receives something
break;
}
case SerialData.Eo:
{
// means reciving ended
break;
}
}
First of all change your line to:
sp.DataReceived += new SerialDataReceivedEventHandler(OnDataReceived);
Second, be aware that DataReceived is fired every time a byte arrives - so the data you read is likely to be a single character each time, and "buffer" will never hold an entire message if you overwrite it every time you handle the event.