2

I am prototyping a sort of Arduino-based docking station for a tablet, using the USB port as connector. This means I need to support to ability to plug/unplug the USB connector while the application on the tablet is running.

The tablet runs a c# application (.net 4.5 on Win7 64 bit) in which I am connecting to the Arduino Uno. When the application is launched I loop all available COM ports using:

var ports = SerialPort.GetPortNames(); // -> [COM3,COM4,COM8]
foreach (var port in ports)
{
     var serial = new SerialPort(portname, baudRate);
     //attempt handshake and connect to right port
}

This work fine, but if I unplug and replug the USB cable and reattempt to reconnect to the Arduino (while the application is still running), the Arduino port (COM8) is no longer listed in:

SerialPort.GetPortNames(); // -> [COM3,COM4] and no COM8

Even restarting the application (with the Arduino replugged) will result in only [COM3,COM4] being listed.

The only way to get it back to work is to unplug and replug the Arduino while the application is not running.

What confuses me is the fact that when I plug in the Arduino Uno after starting the application, the SerialClass does recognize the newly added port and allows me to connect.

The problem only occurs when I unplug and replug the device when the application is running. It seems that despite the ability to reset the COM port (in code or manually in device manager), the SerialClass (and native Win32_SerialPort - I checked this too) do not recognize this, unless I restart the application

What could be the reason for this? And how can I make sure that my application can reconnect to that port? Are there any alternatives to using the SerialPort to handle the USB connector?

dsfgsho
  • 2,731
  • 2
  • 22
  • 39
  • Just a second here, are you using a serial port as a USB port, or a USB port as a serial port? AFAIK, every time you reconnect something to a COM port you need to reboot it. – Shark May 23 '13 at 14:36
  • serial port, I connect things all the time to serail ports with out rebooting. The real serial ports (rs-232) are very robust.(I thought) – John b May 23 '13 at 15:19
  • When the Virtual COM port goes away, so does all its interfaces, and the app connect to them needs to deal with these exceptions and then try to rediscover them. Most apps that use serial ports are old and don't handle the ripping away of what it expected to be a hardware interfaces, often resulting in the resource being lost until reboot. I note that the Arduino's Serial Monitor handles this except fairly well and recovers. It makes alot of noise in its console output. You may want to look at how it does that and port it to C# – mpflaga May 23 '13 at 15:33
  • I found some excellent [code samples](http://dotnet-experience.blogspot.dk/2012/05/resetting-local-ports-and-devices-from.html) for resetting the COM port. However, despite the reset, it still requires the .net application to restart before the port is recognized again. Reconnecting without a restart results in: `System.IO.IOException: The port 'COMx' does not exist.` – dsfgsho May 23 '13 at 16:05
  • possible duplicate of [Releasing a unplugged virtual Serial Port](http://stackoverflow.com/questions/9835881/releasing-a-unplugged-virtual-serial-port) – Hans Passant May 23 '13 at 16:52
  • 1
    have you tried disposing of that instance of SerialPort* and creating a new one when you lose a connection? – DotNetRussell May 23 '13 at 17:29
  • Yes, when I tried to reconnect to the port, I use a new instance of SerialPort. The problem however is the fact that the COMx port simply is not listed in available ports. – dsfgsho May 23 '13 at 17:43
  • @HansPassant I'm not sure if it's the same problem since that question is about difficulties closing the application and having to reboot the entire system to reset the ports. The main issue I am facing here is that although I can reset (meaning disable/enable) the COM port after re-plugging the device; the .net SerialPort class does not seem to re-detect the port although the device manager lists the device on the correct port. I still receive: "System.IO.IOException: The port 'COMx' does not exist". Only if I restart the application and then disable/enable the port, I can reconnect. – dsfgsho May 23 '13 at 22:24
  • 2
    It is the same problem. Every USB driver misbehaves in its own way. Your specific problem is that it doesn't update the registry correctly, the keys that SerialPort uses when it calls GetPortNames(). The message ought to be clear, don't disconnect the cable while the port is in use, there's just no point to it. You don't jerk a flash drive out of a USB port either while Windows is writing to it, treat USB emulators the same way. If this feature is essential to you for some mysterious reason then you need to go shopping for another one with a better driver. – Hans Passant May 23 '13 at 22:35
  • @HansPassant That's what I was afraid of. The problem is that I am prototyping a sort of Arduino-based docking station for a tablet, using the USB port as connector; hence the ad hoc plug/unplug requirement. Any advice or pointers on alternative approaches? Maybe writing a custom driver? – dsfgsho May 23 '13 at 22:51
  • 1
    I gave very specific advice in the linked question. Writing your own driver was certainly not part of that. – Hans Passant May 23 '13 at 22:52

2 Answers2

2

I found a solution that can handle plugging and unplugging a SerialPort.

First of all, it requires the use the SafeSerialPort, which allows you to dispose the serial port properly.

SafeSerialPort serialPort;

private void Connect()
{
    string portname = "COM8";
    serialPort = new SafeSerialPort(portname, 9600);
    serialPort.DataReceived += port_DataReceived;
    serialPort.Open(); 
}

Second, you need to use LibUsbDotNet to detect whether a USB device is connected or disconnected. This will allow you to determine whether to connect to the device or reset the COM port.

public UsbDevice MyUsbDevice;

//Find your vendor id etc by listing all available USB devices
public UsbDeviceFinder MyUsbFinder = new UsbDeviceFinder(0x2341, 0x0001);
public IDeviceNotifier UsbDeviceNotifier = DeviceNotifier.OpenDeviceNotifier();
private void OnDeviceNotifyEvent(object sender, DeviceNotifyEventArgs e)
{
    if (e.Object.ToString().Split('\n')[1].Contains("0x2341"))
    {
        if (e.EventType == EventType.DeviceArrival)
        {
            Connect();
        }
        else if(e.EventType == EventType.DeviceRemoveComplete)
        {
            ResetConnection();
        }
    }
}

Finally, disposing the SerialPort will makes sure it is registered by Windows in HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM, meaning that SerialPort.GetPortNames() can re-detect the port.

private void ResetConnection()
{
    try
    {
        //Send any data to cause an IOException
        serialPort.Write("Any value");
    }
    catch (IOException ex)
    {
        //Dispose the SafeSerialPort
        serialPort.Dispose();
        serialPort.Close();
    }
}

After this process, you can simply reconnect to the COM port when the USB device is connected without the need to restart the application.

Full code:

using LibUsbDotNet;
using LibUsbDotNet.DeviceNotify;
using LibUsbDotNet.Info;
using LibUsbDotNet.Main;    

SafeSerialPort serialPort;

            public SerialPortTest()
            {
                Connect();

                UsbDeviceNotifier.OnDeviceNotify += OnDeviceNotifyEvent;
            }

            private void Connect()
            {
                string portname = "COM8";
                serialPort = new SafeSerialPort(portname, 9600);
                serialPort.DataReceived += port_DataReceived;
                serialPort.Open(); 
            }

            private void ResetConnection()
            {
                try
                {
                    serialPort.Write("Any value");
                }
                catch (IOException ex)
                {
                    serialPort.Dispose();
                    serialPort.Close();
                }
            }


            void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                Console.WriteLine(serialPort.ReadExisting());
            }

            public UsbDevice MyUsbDevice;

            //Vendor ID etc can be found through enumerating the USB devices
            public UsbDeviceFinder MyUsbFinder = new UsbDeviceFinder(0x2341, 0x0001);
            public IDeviceNotifier UsbDeviceNotifier = DeviceNotifier.OpenDeviceNotifier();
            private void OnDeviceNotifyEvent(object sender, DeviceNotifyEventArgs e)
            {
                //if this is your usb device, in my case an Arduino
                if (e.Object.ToString().Split('\n')[1].Contains("0x2341"))
                {
                    if (e.EventType == EventType.DeviceArrival)
                    {
                        Connect();
                    }
                    else
                    {
                        ResetConnection();
                    }
                }
            }
Community
  • 1
  • 1
dsfgsho
  • 2,731
  • 2
  • 22
  • 39
1

So I believe this is happening because your program is caching the address of the USB the first time it is plugged in.

When someone plugs in a device, the hub detects voltage on either D+ or D- and signals the insertion to the host via this interrupt endpoint. When the host polls this interrupt endpoint, it learns that the new device is present. It then instructs the hub (via the default control pipe) to reset the port where the new device was plugged in. ***This reset makes the new device assume address 0, and the host can then interact with it directly; this interaction will result in the host assigning a new (non-zero) address to the device.

Your best bet is to research how to programically flush the address cache of USB devices.

Reference:http://en.wikipedia.org/wiki/USB_hub

DotNetRussell
  • 9,716
  • 10
  • 56
  • 111