On a related note to the previous post, here is a patch that enables the SerialPort.DataReceived event. It is not officially supported in the Linux version of Mono. This workaround works in the following way:
When a port is opened, a new thread is started. It polls the port for incoming data and generates the DataReceived events. The thread exits automatically when the serial port is closed by the application.
Update: The solution has been tested in Ubuntu and OpenEmbedded Linux during the last year. No problems found.
Update: added another way to work around the problem. It does not require recompiling Mono from source, only changes to the application. Link
diff --git a/mcs/class/System/System.IO.Ports/SerialPort.cs b/mcs/class/System/System.IO.Ports/SerialPort.cs index 657d4b1..ad01dbc 100644 --- a/mcs/class/System/System.IO.Ports/SerialPort.cs +++ b/mcs/class/System/System.IO.Ports/SerialPort.cs @@ -578,14 +578,39 @@ namespace System.IO.Ports if (IsWindows) // Use windows kernel32 backend stream = new WinSerialStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable, rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize); - else // Use standard unix backend + else { // Use standard unix backend #endif stream = new SerialPortStream (port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable, rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize); - + + new System.Threading.Thread(new System.Threading.ThreadStart(this.EventThreadFunction)).Start(); + } is_open = true; } + private void EventThreadFunction( ) + { + SerialPortStream _stream = stream as SerialPortStream; + if (_stream == null){ + return; + } + + do + { + try + { + if (_stream.Poll(ReadTimeout)){ + OnDataReceived(new SerialDataReceivedEventArgs(SerialData.Chars)); + } + } + catch + { + return; + } + } + while (is_open); + } + public int Read (byte[] buffer, int offset, int count) { CheckOpen ();
diff --git a/mcs/class/System/System.IO.Ports/SerialPortStream.cs b/mcs/class/System/System.IO.Ports/SerialPortStream.cs index 61ca049..1e9d10c 100644 --- a/mcs/class/System/System.IO.Ports/SerialPortStream.cs +++ b/mcs/class/System/System.IO.Ports/SerialPortStream.cs @@ -127,6 +127,17 @@ namespace System.IO.Ports [DllImport ("MonoPosixHelper", SetLastError = true)] static extern bool poll_serial (int fd, out int error, int timeout); + internal bool Poll(int timeout) + { + CheckDisposed (); + int error; + bool poll_result = poll_serial (fd, out error, read_timeout); + if (error == -1) + ThrowIOException (); + + return poll_result; + } + public override int Read ([In,Out] byte[] buffer, int offset, int count) { CheckDisposed ();
Any reason why this hasn’t been published upstream yet?
I consider it to be a workaround, although it works very well
So what is your preferred method to patch it, just copy the SerialPort.cs file to your solution and patch it there or patch it in the mono source and rebuild the mono source?
I did a custom Mono build with a few more changes. I haven’t tried to implement this in an application, guess the stream object is not accessible.
Hi
I downloaded the mono-master but the SerialPortStream
does not seem to contain the Poll methed. did you implement this your self too?
Hi Thomas,
thanks for noticing, indeed the change was missing. I have updated the article
I have implemented this workaround however it seems to give me very erratic results.
If I do a “readExisting” then it only seems to read half a string,
I have sent “hello world” and I only get back “hell” or “orld” with a read existing.
I then tried to implement sp.BytesToRead to find out if the number of bytes coming back were right and it only has 2 bytes to read for a loop back test of hello world.
Maybe I am implementing your code wrong but I just can’t seem to get it to perform properly.
Hello Sam,
this seems to be a timing problem. ReadExisting is called while the data is still incoming. Are you losing data after multiple events? It is normal to receive “he”, “llo w” “orld” in your example. The exact splitting of the data depends on the OS scheduler, serial port driver, buffer size, baud rate etc. For me it is not a problem – my stream parsers always wait for complete packets that are read in one or more events.
You could provide the relevant part of your application code for a quick review
Antanas
I`m using this code and the first run everything works. However when I close and reopen the application the note collector(this is what i`m communicating with) behaves erratically and the connection falls appart. My hunch is that the thread with the event listener remains orphaned? I tried closing the connection on close so that is_open stops the thread but it doesn`t make any difference. I`ll try setting “is_open” as volatile, but i`m not sure that causes it any ideas?
Hi Pavel,
please enable stepping into Mono source (assuming You are using monodevelop) and put some breakpoints to see if the thread loop finishes properly. Or perhaps your ReadTimeout is set to too long value? In this case the thread would wait for that long to check is_open. It would be better to wake up the thread when the port is closed. Unfortunately I’m not working on that project anymore and don’t have the source/tools readily set up.
The library i`m using sets the Read&Write timeouts to 500 is it likely that it fails while a note is being read and that causes all the problems? I managed to decompile this library i`ll try setting them to a bigger value to test
btw thanks for the fast response 🙂 much appreciated you`re basically the only useful info on this matter
Hi! I tried to apply these patches but I get an error “fatal: corrupt patch at line 46”. The command I use is
git apply --ignore-space-change --ignore-whitespace changes.diff
Any hints? For now I will be applying the patch mnually, but I would like to know what I’m doing wrong…
Thanks!
Hi,
perhaps you could try adding a new line at the end of the patch file. Line 46 is the last line, just guessing. Please let me know if you find a working solution, I’ll update the post
The first diff applied successfully after appending a newline. The second one fails on line 6… I applied the second patch manually. Thanks for your quick response!
fatal: corrupt patch at line 6
C:UsersFelixDocumentsGitHubmono [mono-2-10-9 +1 ~1 -0 !]>
I’ve successfully implemented this class on a linux machine. Now i need to use the same code on a windows machine, but it does not seem to work. The windows is using Mono instead of .NET, as it’s running in the Godot Game engine, which ships Mono.
Any ideas?