jcurl / RJCP.DLL.SerialPortStream

SerialPortStream is an independent implementation of System.IO.Ports.SerialPort and SerialStream for better reliability and maintainability. Default branch is 2.x and now has support for Mono with help of a C library.
Microsoft Public License
639 stars 199 forks source link

Device Error #44

Closed overthrowrobotics closed 6 years ago

overthrowrobotics commented 6 years ago

I'm using an arduino style device (Teensy 3.5) at 115200 on COM3. I can open it just find with the Arduino monitor and it just prints an incrementing number on new lines (1, 2, 3, etc). Am I missing something really simple here?

IOException: Device Error RJCP.IO.Ports.SerialPortStream.ReadCheckDeviceError (System.Boolean immediate) (at <98240c6c7bfd40d18eca5b369f29ae84>:0) RJCP.IO.Ports.SerialPortStream.ReadTo (System.String text) (at <98240c6c7bfd40d18eca5b369f29ae84>:0) RJCP.IO.Ports.SerialPortStream.ReadLine () (at <98240c6c7bfd40d18eca5b369f29ae84>:0) (wrapper remoting-invoke-with-check) RJCP.IO.Ports.SerialPortStream:ReadLine () serial.Start () (at Assets/serial.cs:24)

I'm doing this from Unity 2017 running .NET 4.6.

using RJCP.IO.Ports;

SerialPortStream src = new SerialPortStream("COM3", 115200); // This method runs once at startup. void Start () {

    src.Open();
    string srcString = src.ToString();

    Debug.Log(srcString); // same as Console.Write
    Debug.Log(src.ReadLine().ToString());

}

// this runs once per frame until the program exits. void Update() { Debug.Log(src.ReadLine().ToString()); }

jcurl commented 6 years ago

The problem is that there was an unexpected error while reading from the serial port and the reader/writer thread has died. That's why you get the exception. At the very least, this page on tracing from the wiki will help.

There it should be possible to determine why the thread died. Minimum level is warning, verbose if you can.

Speculating (I have no idea what a teensy 3.5 is), but I would image it's a USB connection. Check that the USB connection is stable. If there was an error reported by the OS, the software generally doesn't know how to retry and it returns an error.

pigrew commented 6 years ago

@jcurl, relating to your speculation, I'm trying to handle disconnection events in my code. Upon disconnect, I see the serial port thread exiting, but I don't see any way to receive a callback when this event happens. Is the proper thing for me to do to poll the "CanWrite" property in a background thread and to handle the IOException if that happens? Could the ErrorReceived callback be extended to be called when a disconnect event happens?

(In my application, I very rarely write to the serial port; I'm mostly just logging status updates, but I want to warn the used if a disconnection happens.)

jcurl commented 6 years ago

The SerialPortStream cannot know why the device reported an error and the error code that is returned may differ depending on the operating system. It was never designed this way, as a serial port may be emulated, physical that is not hotpluggable or hotpluggable. Important is on the Read method, you finally get 0 that indicates that the stream is closed. A second read results in an exception. Basically, it's the 0 from reading for a blocking connection that indicates no data is available.

If you need to know if a USB device is removed, you will have to refer to the documentation for the USB subsystem to determine hotplugging events. The Serial subsystem simply doesn't provide that information and this is not the scope of the SerialPortStream.

In your case, if you're only logging rarely, you'll be blocked on the Read(). If you have an infinite timeout, zero indicates the device is closed. Log to the user and report it. If you have timeouts, on receiving the exception log to the user and report it. Then try to reopen the port.

Note in my testing, I've seen that sometimes the Linux Hotplug system will assign a new port device name, rather than reuse the old port name (and the old port name seems to be "stuck"). That's entirely up to the kernel, not much that can be done here, except to query the kernel (see the code in GetPortNames in libnserial.so on how I do this).