A followup to an older post of mine: http://antanas.veiverys.com/enabling-serialport-datareceived-event-in-mono/. The previous approach required rebuilding Mono with a small patch. Here is a derived class that does the same.
Advantages:
- a standard Mono distribution can be used
Disadvantages:
- reflection is used to access three private members by their names. In case of future changes in Mono the application will break;
- currently DataReceived(object sender, SerialDataReceivedEventArgs e) event handler gets null value for parameter e. The only constructor of Mono SerialDataReceivedEventArgs class is marked internal and I have not figured out how to create the objects yet. Need more sleep and coffee;
- SerialPort.Open() is not a virtual method, therefore I have hidden the method in order to add the thread creation. Keep in this in mind: http://stackoverflow.com/questions/856449/overloading-overriding-and-hiding. The port variable must be declared as EnhancedSerialPort type or the wrong Open() method will get called;
TO DO:
- test the code running by both Microsoft .NET and MONO
Source code:
// /* // Copyright 2013 Antanas Veiverys www.veiverys.com // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // */ // using System; using System.IO.Ports; using System.ComponentModel; using System.IO; using System.Runtime.InteropServices; using System.Reflection; using System.Threading; namespace testapp { public class EnhancedSerialPort : SerialPort { public EnhancedSerialPort () :base() { } public EnhancedSerialPort (IContainer container) : base (container) { } public EnhancedSerialPort (string portName) : base(portName) { } public EnhancedSerialPort (string portName, int baudRate) :base(portName, baudRate) { } public EnhancedSerialPort (string portName, int baudRate, Parity parity) : base(portName, baudRate, parity) { } public EnhancedSerialPort (string portName, int baudRate, Parity parity, int dataBits) : base(portName, baudRate, parity, dataBits) { } public EnhancedSerialPort (string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits):base(portName, baudRate, parity, dataBits,stopBits) { } // private member access via reflection int fd; FieldInfo disposedFieldInfo; object data_received; public new void Open () { base.Open(); if (IsWindows == false) { FieldInfo fieldInfo = BaseStream.GetType().GetField("fd", BindingFlags.Instance | BindingFlags.NonPublic); fd = (int)fieldInfo.GetValue(BaseStream); disposedFieldInfo = BaseStream.GetType().GetField("disposed", BindingFlags.Instance | BindingFlags.NonPublic); fieldInfo = typeof(SerialPort).GetField("data_received", BindingFlags.Instance | BindingFlags.NonPublic); data_received = fieldInfo.GetValue(this); new System.Threading.Thread(new System.Threading.ThreadStart(this.EventThreadFunction)).Start(); } } static bool IsWindows { get { PlatformID id = Environment.OSVersion.Platform; return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported } } private void EventThreadFunction( ) { do { try { var _stream = BaseStream; if (_stream == null){ return; } if (Poll (_stream, ReadTimeout)){ OnDataReceived(null); } } catch { return; } } while (IsOpen); } void OnDataReceived (SerialDataReceivedEventArgs args) { SerialDataReceivedEventHandler handler = (SerialDataReceivedEventHandler) Events [data_received]; if (handler != null) { handler (this, args); } } [DllImport ("MonoPosixHelper", SetLastError = true)] static extern bool poll_serial (int fd, out int error, int timeout); private bool Poll(Stream stream, int timeout) { CheckDisposed (stream); if (IsOpen == false){ throw new Exception("port is closed"); } int error; bool poll_result = poll_serial (fd, out error, ReadTimeout); if (error == -1) { ThrowIOException (); } return poll_result; } [DllImport ("libc")] static extern IntPtr strerror (int errnum); static void ThrowIOException () { int errnum = Marshal.GetLastWin32Error (); string error_message = Marshal.PtrToStringAnsi (strerror (errnum)); throw new IOException (error_message); } void CheckDisposed (Stream stream) { bool disposed = (bool)disposedFieldInfo.GetValue(stream); if (disposed) { throw new ObjectDisposedException (stream.GetType().FullName); } } } }
Test application:
// /* // Copyright 2013 Antanas Veiverys www.veiverys.com // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // */ // using System; using System.IO.Ports; using System.ComponentModel; using System.IO; using System.Runtime.InteropServices; using System.Reflection; using System.Threading; namespace testapp { class MainClass { static EnhancedSerialPort port; public static void Main (string[] args) { port = new EnhancedSerialPort("/dev/ttyUSB0", 9600); port.DataReceived += HandlePortDataReceived; port.ReadTimeout = 100; port.Open(); while (Console.ReadKey(true).KeyChar != 'x'){ port.Write("012"); } port.Close(); } static void HandlePortDataReceived (object sender, SerialDataReceivedEventArgs e) { while (port.BytesToRead > 0) { int bt = port.ReadByte(); Console.WriteLine("{0}", bt); } } } }
Is it working for you?
Great article! Your derived class helped me port some of our .NET code to Mono really easily.
Hello,
I have been trying to re-write a .NET based C# application under Monodevelop which required SerialPort implementation. The original SerialPort was giving problem, but your solution solved hanging problem.
I used it under Ubuntu 12.04 with Monodevelop Ver 4.0 & 5.01 (and also on default Monodevelop) and it worked well for following devices.
1. Beagleboard Rev. C3.
2. EZ430 dongle (A Texas Instruments product).
3. TI-Launchpad.
Thanks for this beautiful post which is helpful for a lot of people struggling in the implementation.
—
Regards,
VKSALIAN
Hello,
EDIT : I am facing below problem,
1. Sometimes the sent bytes are echo’d back as data recieved. i.e. recievedData = sentData.
2. Sometimes it failes to catch all the characters and hence there is a loss of data
Please suggest if there exists any remedies for this.
—
Thanks,
VKSALIAN
Hi,
you could check your physical connection – may there be a short between TX and RX? Normally the sent bytes should not be received. Try disconnecting your cable and device and just send a bunch of data. If you do not receive anything back, the problem is in your device, or the cable.
Antanas
Hello,
Finally, I got the code working. The problem was with UART-USB hardware itself which is not working properly under Linux. So I used FT232R chip to solve this problem. After this, I tested it with the same code and it worked as per my expectations. Thanks for the help.
—
Thanks & Regards,
VKSALIAN
Hi Antanas,
You solution works for me too, thank you. Since you have copyright over, can you make modification on official Mono SerialPort source version?
Hello! Thank you for a great class.
So what about event args which’ constructor is marked internal?
Have you solved this?
Thanks!
Hi Antanas,
Thank you for your solution it works for me also. Since you have copyright did you think to publish your solution into official Mono framework?
Hi Ervin,
I do not believe this should go into Mono framework. This is just a workaround that seems to work with older Mono versions without patching them. The missing functionality should be implemented properly, but I had not figured out how at the time. Unfortunately I don’t work on the project that needed Mono anymore, so this is not a pressing issue for me.
Thank you for your reply. Ok I agree with you about implementing your workaround into the Mono. Even trunk version of Mono don’t have right implementation of OnDataReceived event so this issue is still active.
Can I use your code with your name in Firmata project (https://github.com/SolidSoils/Arduino) and make pull request?
Hi Ervin,
yes, of course you can use it in your project.
Are there any updates to the concepts described in this article over the past two years, for example different approaches and/or updates to Mono under Linux.
Hi Bob,
no I don’t have any updates.
When running in a Windows form under xinit the window manager doesn\’t exit when the application terminates. When running with the standard SerialPort it goes back to the command prompt. Any ideas why this might happen? Thanks.
Sorry about the late reply, the spam filter got your comment.
I would suspect that the thread does not terminate. Do you close the port before exiting the main process?
Hello Antanas, your solution works perfectly in a Raspberry.
I’m developing a program that scans a sensor array trough Raspberry serial port . I begin to develop in C# and using Mono on Raspbian and works until the serial reception, because I was using an event handler for reception. Later I found that serial port over Mono doesn’t work with event handler UNTIL I found your solution.
Just I have to replace with your class, EnhancedSerialPort, and works like it was over Windows.
Thanks a lot.
Giovanni G
Thank you – works great!
Just dropping a note to say thanks! Saved me a bunch of messing around trying to get something going on a Raspberry Pi.
Hey! I\’m trying to expand on your work. Check out my question on StackOverflow, if you have the time. Thank you so much for publishing this!!!
http://stackoverflow.com/questions/42393784/receivedbytesthreshold-on-mono-serial-port
Hi guys,
I am very curious if anyone can help me out..
First of, thanks for the great work! Pleas keep it up!
Now to my problem:
Looking for an event handler that works with mono, i found your code and was able to use it out of the box on a Mac System and am really greatfull for that.
Running this on a windows machine in Mono develop also works great!! Out of curiosity i put the script in Unity 3d to read serial data from there, too (unity beeing multiplatform and using mono 4.6 for coding/building). While it ran out of the box on Mac, nothing works on windows -> same code, but no data…
Has anyone any idea what to do/encountered similiar poblems?
Recently started doing some mono developing. I ported an older serial port event driven windows program with the use of your EnhancedSerialPort. Works perfectly. Thank you … with one exception… When running in Mono, the program hangs when I go to close it. It just stops responding. I need to kill it from the command line.
I’ve tried putting a call to Dispose() in the FormClosing() event, but, to no avail.
Any tips or advice is much appreciated.
Hi
can you confirm that your program closes properly when using the normal Mono serial port, not my class?
Antanas
Awesome solution, exactly what I needed.
You saved me hours of struggle and lots of frustration.
I hope the universe rewards you 🙂
Thanks
Hi
I am going to sound very naive, but can I get help how to implement this codes. I am completely new to RPI, & C#.
I am trying to do echo of a line on RPI3B+ & RPI4, the problem is I can send line of data I can see them on minicom, but I do receive it at all.. as I read the serial port data receiving even doesn’t trigger in mono.
I have written code in VS2022, in C#, having a winform application. I have a text box where I write data as dataout and the same has been looped back as pin Tx and Rx are looped together but nothing received in textbox for as datain.
Please help me out.
Tirath