How do I use the SerialDataRecievedEventHandler routine? (C#) [duplicate] - c#

This question already has answers here:
SerialPort not receiving any data
(3 answers)
Closed 7 years ago.
I am using this windows library: System.IO.ports
I have a class called ServerObject and it has a public member: sPort of type SerialPort.
ServerObject.sPort.PortName = "COM4";
ServerObject.sPort.BaudRate = 460800;
ServerObject.sPort.Parity = 0;
ServerObject.sPort.DataBits = 8;
ServerObject.sPort.Open();
ServerObject.sPort.DataReceived += new SerialDataReceivedEventHandler(ServerObject.sPort_DataReceived);
The above code runs once in the main. I am thinking that the last line here is the culprit.
The below code is the public method for the ServerObject class that that last line is calling.
public void sPort_DataReceived(object sender, SerialDataReceivedEventArgs e) //event for data appearing on the serial port.
{
try
{
string payload = null;
payload = sPort.ReadLine(); //extract incoming message
this.DebugMessageQueue.Enqueue(payload);
}
catch (Exception ex)
{
}
}
Whenever something gets onto that queue, it gets printed. I tested that. However, nothing is happening. Any ideas? I think that I am not understanding this event handler. Is it suppose to be in a loop? I thought that the event handler would call sPort_DataRecieved whenever data appears on the port. Does it only run once?

From MSDN https://msdn.microsoft.com/de-de/library/system.io.ports.serialport.datareceived(v=vs.110).aspx:
The DataReceived event is not guaranteed to be raised for every byte received. Use the BytesToRead property to determine how much data is left to be read in the buffer.
Obviously your code will not work as expected due to this limitation. I suggest using a polling routine instead, which cyclically checks the input buffer.
Also, I suggest you test your serial connection in a simple and reliable way first. Avoid complicated elements like events. Send/receive data by writing into and reading from the buffer. Use a local terminal program (like HW Group Hercules, which can also be used for TCP client/Server and UDP) to exchange data with your application...

Related

How to bind web socket to events in .NET C#

I am writing a plugin for the Elgato Stream Deck. The Stream Deck uses a web socket for communication with plugins. I am trying to write my plugin as a console app in C#. The "compiled plugin" example provided by Elgato is broken (this GitHub issue points to one problem, and there appear to be more) and does not appear to run - so I have been digging through the code to make sense of it and translate it to C# without using Visual Studio debugging. I did find this, however (link to code file on GitHub):
void ESDConnectionManager::Run()
{
try
{
// Create the endpoint
mWebsocket.clear_access_channels(websocketpp::log::alevel::all);
mWebsocket.clear_error_channels(websocketpp::log::elevel::all);
// Initialize ASIO
mWebsocket.init_asio();
// Register our message handler
mWebsocket.set_open_handler(websocketpp::lib::bind(&ESDConnectionManager::OnOpen, this, &mWebsocket, websocketpp::lib::placeholders::_1));
mWebsocket.set_fail_handler(websocketpp::lib::bind(&ESDConnectionManager::OnFail, this, &mWebsocket, websocketpp::lib::placeholders::_1));
mWebsocket.set_close_handler(websocketpp::lib::bind(&ESDConnectionManager::OnClose, this, &mWebsocket, websocketpp::lib::placeholders::_1));
mWebsocket.set_message_handler(websocketpp::lib::bind(&ESDConnectionManager::OnMessage, this, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2));
websocketpp::lib::error_code ec;
std::string uri = "ws://127.0.0.1:" + std::to_string(mPort);
WebsocketClient::connection_ptr connection = mWebsocket.get_connection(uri, ec);
if (ec)
{
DebugPrint("Connect initialization error: %s\n", ec.message().c_str());
return;
}
mConnectionHandle = connection->get_handle();
// Note that connect here only requests a connection. No network messages are
// exchanged until the event loop starts running in the next line.
mWebsocket.connect(connection);
// Start the ASIO io_service run loop
// this will cause a single connection to be made to the server. mWebsocket.run()
// will exit when this connection is closed.
mWebsocket.run();
}
catch (websocketpp::exception const & e)
{
// Prevent an unused variable warning in release builds
(void)e;
DebugPrint("Websocket threw an exception: %s\n", e.what());
}
}
This appears to be using some library called boost.asio? The closest thing I can find in C# is the MessageWebSocket from Windows.Networking.Sockets (documentation here) but this appears to be only for Windows RT?
How do I create something analogous to this in a .NET console app? I have found references to System.Net.WebSockets.ClientWebSocket (documentation here), which appears to be the class I need, but I am not sure.
I have found a few samples (such as this one, and this one - both of which use yet another socket class, Socket) that show how to use Sockets in general, but they do not seem event-driven. They seem to send, and then immediately receive data. I want a web socket that calls local methods (akin to event handlers) when events/data are received by the socket. I believe this is what is going on in the sample C++ code above (specifically Open, Fail, Close, and Message). My understanding is that I need to do some work ("register" with the Stream Deck) on the web socket "Open" event, and then handle the web socket "Message" event and parse out what event the Stream Deck is reporting happened.
If you scroll down some lines in your example https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket?view=net-7.0 you will find a example called: Asyncronous Mode that handles sending and receiving separately.
Send part
int bytesSent = 0;
while (bytesSent < requestBytes.Length)
{
bytesSent += await socket.SendAsync(requestBytes.AsMemory(bytesSent), SocketFlags.None);
}
If you do not want the program to lock up, the easiest way would be to start a thread for listening maybe?

Multithreading using AsyncCallback and GUI controls

Multithread programming is a new concept for me. I’ve done a bunch of reading and even with many examples, I just can’t seem to figure it out. I'm new to C# and programming.
I have a winform project with lots of custom controls I’ve imported and will utilize many tcpclients. I’m trying to get each control to be hosted on it’s own separate thread. Right now, I’m trying to get 1 control to behave appropriately with it’s own thread.
I'll show you what I have and then follow up with some questions regarding guidance.
string asyncServerHolder; // gets the server name from a text_changed event
int asyncPortHolder; // gets the port # from a text_changed event
TcpClient wifiClient = new TcpClient();
private void btnStart_Click(object sender, EventArgs e)
{
... // variable initialization, etc.
... // XML setup, http POST setup.
send(postString + XMLString); // Content to send.
}
private void send(string msg)
{
AsyncCallback callBack = new AsyncCallback(ContentDownload);
wifiClient.BeginConnect(asyncServerHolder, asyncPortHolder, callBack, wifiClient);
wifiClient.Client.Send(System.Text.Encoding.ASCII.GetBytes(msg));
}
private void ContentDownload(IAsyncResult result)
{
if (wifiClient.Connected)
{
string response4 = "Connected!!"; //debug msg
byte[] buff = new byte[1024];
int i = wifiClient.Client.Receive(buff);
do
{
response1 = System.Text.Encoding.UTF8.GetString(buff, 0, i);
} while (response1.Length == 0);
response2 = response1.Substring(9, 3); // pick out status code to be displayed after
wifiClient.Client.Dispose();
wifiClient.Close();
}
}
If you're knowledgeable about this, I bet you see lots of problems above. As it stands right now, I always get an exception one my first iteration of running this sequence:
"A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied"
Why is this? I have confirmed that my asyncServerHolder and my asyncPortHolder are correct. My second iteration of attempting allowed me to see response4 = "Connected!!" but I get a null response on response1.
Eventually I'd like to substitute in my user controls which I have in a List. I'd just like to gracefully connect, send my msg, receive my response and then allow my form to notify me from that particular control which plays host to that tcp client. My next step would be link up many controls.
Some questions:
1) Do I need more TCP clients? Should they be in a list and be the # of controls I have enabled at that time of btnStart_Click?
2) My controls are on my GUI, does that mean I need to invoke if I'm interacting with them?
3) I see many examples using static methods with this context. Why is this?
Thanks in advance. All criticism is welcome, feel free to be harsh!
BeginConnect returns immediately. Probably, no connection has been established yet when Send runs. Make sure that you use the connection only after having connected.
if (wifiClient.Connected) and what if !Connected? You just do nothing. That's not a valid error recovery strategy. Remove this if entirely.
In your read loop you destroy the previously read contents on each iteration. In fact, you can't split up an UTF8 encoded string at all and decode the parts separately. Read all bytes into some buffer and only when you have received everything, decode the bytes to a string.
wifiClient.Client.Dispose();
wifiClient.Close();
Superstitious dispose pattern. wifiClient.Dispose(); is the canonical way to release everything.
I didn't quite understand what "controls" you are talking about. A socket is not a control. UI controls are single-threaded. Only access them on the UI thread.
Do I need more TCP clients?
You need one for each connection.
Probably, you should use await for all blocking operations. There are wrapper libraries that make the socket APIs usable with await.

C# "using" SerialPort transmit with data loss

I'm new to this forum, and I have a question that has been bothering me for a while.
My setup is a serial enabled character display connected to my pc with a usb/uart converter. I'm transmitting bytes to the display via the serialPort class in a separate write buffer thread in a C++ style:
private void transmitThread(){
while(threadAlive){
if(q.Count > 0){ // Queue not empty
byte[] b = q.Dequeue();
s.Write(b,0,b.Length);
System.Threading.Thread.Sleep(100);
}
else{ // Queue empty
System.Threading.Thread.Sleep(10);
}
}
}
Assuming the serial port is already opened, this works perfectly and transmits all the data to the display. There are though no exception handling at all in this snippet. Therefore I was looking into implementing a typical C# feature, the 'using' statement and only opening the port when needed, like so:
private void transmitThread(){
while(threadAlive){
if(q.Count > 0){ // Queue not empty
byte[] b = q.Dequeue();
using(s){ //using the serialPort
s.Open();
s.Write(b,0,b.Length);
s.Close();
}
System.Threading.Thread.Sleep(100);
}
else{ // Queue empty
System.Threading.Thread.Sleep(10);
}
}
}
The problem with this function is, that it only transmits a random amount of the data, typically about one third of the byte-array of 80 bytes. I have tried different priority settings of the thread, but nothing changes.
Am I missing something important, or do I simply close the port too fast after a transmit request?
I hope you can help me. Thanks :)
No, that was a Really Bad Idea. The things that go wrong, roughly in the order you'll encounter them:
the serial port driver discards any bytes left in the transmit buffer that were not yet transmitted when you close the port. Which is what you are seeing now.
the MSDN article for SerialPort.Close() warns that you must "wait a while" before opening the port again. There's an internal worker thread that needs to shut down. The amount of time you have to wait is not specified and is variable, depending on machine load.
closing a port allows another program to grab the port and open it. Serial ports cannot be shared, your program will fail when you try to open it again.
Serial ports were simply not designed to be opened and closed on-the-fly. Only open it at the start of your program, close it when it ends. Not calling Close() at all is quite acceptable and avoids a deadlock scenario.
I think you're missing the point of the using block. A typical using block will look like this:
using (var resource = new SomeResource())
{
resource.DoSomething();
}
The opening happens at the very beginning. Typically as part of the constructor. But sometimes on the first line of the using block.
But the big red flag I see is that the closing happens automatically. You don't need the .Close() call.
If the successful operation of your serial device is dependent on the calls to Thread.Sleep then perhaps the thread is being interrupted at some point, sufficient to make the data transmission out of sync with the device. There would most likely be ways to solve this but the first thing I would do is try to use the .NET SerialPort class instead. The Write method is very similar to what you want to do, and there are C++ code examples in those articles.

C# serial port problem - too simple to fail, but

Ok, this should be dirt simple. I'm trying to read charactes from a serial device. It's such that if I send a space character, it echos back a string of numbers and EOL. That's it.
I'm using Unity 3.3 (.Net 2.0 support), and the 'serial port' is a Prolific serial-to-USB adaptor. BTW: Using Hyperterminal, it all works perfectly, so I know it's not driver nor hardware.
I can open the port ok. It seems I can send my space with port.Write(" "); But if I even TRY to call ReadChar, ReadByte, or ReadLine (like polling), it freezes up until I unplug the USB, and my console output shows nothing (exceptions were caught).
So instead I set up a DataReceviedHandler, but it's never called.
I've read some posts where people have done just this type of thing with Arduinos etc. (this is not an Arduino but hey), using nothing more than ReadLine. Their code does not work for me (and no answers thus far from those authors).
So, any tips? Do I need to use a different thread? If you know any Unity (Mono) coding, any tips along those lines greatly appreciated.
This code a mashup from http://plikker.com/?p=163 and http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx#Y537
using UnityEngine;
using System.Collections;
using System.IO.Ports;
using System;
public class SerialTest : MonoBehaviour {
SerialPort stream;
void Start () {
try {
stream = new SerialPort("COM3", 9600);
stream.Parity = Parity.None;
stream.StopBits = StopBits.One;
stream.DataBits = 8;
stream.Handshake = Handshake.None;
stream.DataReceived += new SerialDataReceivedEventHandler(DataReceviedHandler);
stream.Open();
Debug.Log("opened ok"); // it DOES open ok!
} catch (Exception e){
Debug.Log("Error opening port "+e.ToString()); // I never see this message
}
}
void Update () { // called about 60 times/second
try {
// Read serialinput from COM3
// if this next line is here, it will hang, I don't even see the startup message
Debug.Log(stream.ReadLine());
// Note: I've also tried ReadByte and ReadChar and the same problem, it hangs
} catch (Exception e){
Debug.Log("Error reading input "+e.ToString());
}
}
private static void DataReceviedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender; // It never gets here!
string indata = sp.ReadExisting();
Debug.Log("Data Received:");
Debug.Log(indata);
}
void OnGUI() // simple GUI
{
// Create a button that, when pressed, sends the 'ping'
if (GUI.Button (new Rect(10,10,100,20), "Send"))
stream.Write(" ");
}
}
Events are not implemented in Mono SerialPort class, so you won't get any notifications, you have to perform (blocking) read explicitly. Other possible problem - I'm not sure how Unity Behaviours work, are you certain all methods accessing the SerialPort are invoked on the same thread? And you are not disposing you port object, this will also cause problems.
Make sure that you are opening the right port, using correct settings. Here is an example of how you could configure it:
serial = new SerialPort();
serial.ReadBufferSize = 8192;
serial.WriteBufferSize = 128;
serial.PortName = "COM1";
serial.BaudRate = 115200;
serial.Parity = Parity.None;
serial.StopBits = StopBits.One;
// attach handlers
// (appears to be broken in some Mono versions?)
serial.DataReceived += SerialPort_DataReceived;
serial.Disposed += SerialPort_Disposed;
serial.Open();
I recommend the open source RealTerm terminal, it has a rich set of features and can help you debug. Try writing a byte manually using such software, and if it works, then the problem is in your program. Otherwise it might be a driver problem (but more likely it isn't).
[Edit]
Calling SerialPort.ReadLine is actually supposed to block the thread until SerialPort.NewLine is received. Also ReadChar and ReadByte will hang until at least one byte is received. You need to make sure that you are actually receiving characters from the other side, and you won't be receiving them if your app is stuck and cannot send the space.
Since I never used Unity, I am not sure how Update is called, but I am presuming it's fired on a foreground thread in regular intervals (otherwise your app wouldn't freeze).
The example that you linked (Arduino and Unity example) shows that Arduino is sending the data continuously, and that is why their Update method is constantly receiving data (no space character needs to be sent towards the device). If they unplug the device, their app will hang just as well.
Well, maybe not, because in .NET 1.1, default value for ReadTimeout was not infinite, like it is in .NET 2.0.
So, what you can do is:
a. Set the ReadTimeout property to a reasonable value. Default in .NET 2.0 is InfiniteTimeout, which doesn't suit your needs. Cons: your update method will still hang for a while on each call, but not infinitely.
b. Someone said that events are not implemented in MONO SerialPort, so I guess using DataReceived only is not an option.
c. Move your sending logic to the Update method also, so that you don't read data at all, until it's time to read it:
private volatile bool _shouldCommunicate = false;
void Update ()
{
if (_shouldCommunicate) // this is a flag you set in "OnGui"
{
try {
stream.Write(" ");
Debug.Log(stream.ReadLine());
} catch (Exception e){
Debug.Log("Error reading input "+e.ToString());
}
}
}
void OnGUI() // simple GUI
{
if (GUI.Button (new Rect(10,10,100,20), "Send"))
_shouldCommunicate = true;
}
Note that, if your device is not sending data, it will also block at stream.ReadLine(), so make sure your ReadTimeout is set to a reasonable value. You will also want to stop sending at some point, but I leave that to you.
d. Send the space in OnGui like you are doing now, but always check if there is data in your buffer before reading it:
void Update () { // called about 60 times/second
try {
// call our new method
Debug.Log(ReadLineNonBlocking());
} catch (Exception e){
Debug.Log("Error reading input "+e.ToString());
}
}
private StringBuilder sb = new StringBuilder();
string ReadLineNonBlocking()
{
int len = stream.BytesToRead;
if (len == 0)
return "";
// read the buffer
byte[] buffer = new byte[len];
stream.Read(buffer, 0, len);
sb.Append(ASCIIEncoding.ASCII.GetString(buffer));
// got EOL?
if (sb.Length < 2 ||
sb[sb.Length-2] != '\r' ||
sb[sb.Length-1] != '\n')
return "";
// if we are here, we got both EOL chars
string entireLine = sb.ToString();
sb.Length = 0;
return entireLine;
}
Disclaimer: this is directly out of my head, untested, so there may be some syntax errors which I am sure you will handle.
Maybe your problem is the configuration of the serial port. It is important not only to check for BaudRate or StopBits. Also you should configure DTR, RTS, Handshake, everything. This is important cause maybe another program set some ugly values and the configuration must be explicitly set at every start or some settings of the old connection can run you into trouble.
Also maybe take a look into one of these tools:
com0com
Serial Port Monitor
They can help you to stub your serial interface or to take a deeper look into the connection. Also maybe try to talk to your serial device by using HyperTerminal or some similar tool that's proven to work.
Had similar problem with Mono, upgrading to 2.6.7 helped.
Do not mix data event and blocking read. What do you expect to happen if data arrives? That both the read method and the event should get the same received data?
You should also read about:
CTS: http://en.wikipedia.org/wiki/RS-232_RTS/CTS#RTS.2FCTS_handshaking
DTR: http://en.wikipedia.org/wiki/Data_Terminal_Ready
Small serial port tutorial describing all states: http://www.wcscnet.com/Tutorials/SerialComm/Page1.htm
The standard c# System.IO.Pots.SerialPort sucks big time. I suggest to give RJCP.DLL.SerialPortStream library a try. Synchronous read/write is super easy with this delightful library too, no need to jump through the loops with delegate and listeners.

Creating and keeping a stream alive in c#

I'm using the pretty nifty nmeasharp project to decipher an NMEA stream I'm receiving on a serial port in c#. It all works fine out of the box, but I want to mirror the data to an IP address, and am getting stuck.
The nmeasharp package gets it's data from any stream, so I used to hook it up to the serial port stream, which worked fine:
_nmeaParser.Source = serialport.BaseStream;
Now, I want to use the serial port event to trigger my own routine, where I can redirect the data, so i remove the assignment above, and set:
_serialport.DataReceived += new serialDataReceivedEventHandler(HandleNewSerialPortData);
This event is triggered, and the method gets called. All good at this point, but the nmeasharp code is still looking for a stream to listen to, since I haven't assigned it to a stream anymore.
The following method is where I need to set up a stream to nmeasharp, and write whatever new data the serial port has just received out to that stream.
private void HandleNewSerialPortData(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
this.serialDataAvailable(indata); // raise event that writes string to IP
if (_nmeaParser.Source == null) _nmeaParser.Source = new MemoryStream(840);
if (_nmeaParser.Source.CanWrite) _nmeaParser.Source.Write(ASCIIEncoding.UTF8.GetBytes(indata), 0, indata.Length);
// Unsuccessful attempts
// MemoryStream s = new MemoryStream(ASCIIEncoding.UTF8.GetBytes(indata));
// s.CopyTo(_nmeaParser.Source);
// sp.BaseStream.CopyTo(this.NmeaDataStreamFromSerialPort);
}
I've tried several variations of trying to write to the nmeasharp stream, but none work. One that showed promise was initialising a new stream every time, but that meant that the stream was closed after every DataReceived event, which truncated and missed out serial messages. The (unsuccessful) code was:
_nmeaParser.Source = new MemoryStream(ASCIIEncoding.UTF8.GetBytes(indata));
I've read lots of tutorials, read all the msdn documentation I could find, and still can't get this simple thing working. This has to be easy, right...?
Edit: I would like to keep the nmeasharp code stock if possible, as it works fine, and as the serial data isn't always ASCII, would like to keep it binary (streams) rather than sending it the data as a string. I can fix up the IP redirection for binary later.
Thanks.
I would try to create two streams. Read from the serial stream manually, and copy to both streams.
Set the nmeaParser to use one of them, and have the IP handler read from the second one.
You can look here for a good solution on how to copy streams.

Categories