26

FINAL UPDATE

It was our firmware the whole time. Embarrassing to a degree, but I'm happy we can move forward and I can put learning Java off for another day. My answer is below.

UPDATE

So I have more or less given up on this. I think it is a bug that goes down to the API, but I have neither the time, resources nor skill-set to get to the bottom of it. I think there exists some hardware to whom Windows just gives the middle finger. I have downloaded Eclipse, switched to Java and will try to see if that works. If not, you'll see me back here. However, I would absolutely love to solve this and so if anyone has the time or inclination to dig deep into this one, I'd love to see what you come up with. Obviously I will be checking back here from time to time. Please make sure you '@' me in your comments so I am alerted.


ORIGINAL POST

I know there are a few other people dealing with this issue, but I was hoping someone could help me. I am trying to connect to a COM port, but I am getting an I/O exception when I try to use the serialport.Open() command:

System.IO.IOException: The parameter is incorrect.

   at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
   at System.IO.Ports.InternalResources.WinIOError()
   at System.IO.Ports.SerialStream.InitializeDCB(Int32 baudRate, Parity parity, Int32 dataBits, StopBits stopBits, Boolean discardNull)
   at System.IO.Ports.SerialStream..ctor(String portName, Int32 baudRate, Parity parity, Int32 dataBits, StopBits stopBits, Int32 readTimeout, Int32 writeTimeout, Handshake handshake, Boolean dtrEnable, Boolean rtsEnable, Boolean discardNull, Byte parityReplace)
   at System.IO.Ports.SerialPort.Open()
   at *programtitlehere.cs*:line 90

I am using a Stellaris LM4F232 to emulate a COM port. I can open, access and I get good results using Termite (a terminal program), but whenever I try with Visual Studio it won't even connect, and I get this error. Now I don't even really know what this error means and despite trying to read elsewhere, I still feel lost.

Can anyone explain to me what is happening here and maybe I can begin to try to figure this out? I can include more code, but to be honest there isn't much there; all the properties of the serial port device are as normal, and it is only happening with this device (I can use an MSP430 no problem with the same details).

My code is shown below for people who would like to see it (note this is just a 'sandbox', not the actual program, but the symptoms are identical):

try
{
    serialPort1.PortName = "COM5";
    serialPort1.Open();
    if (serialPort1.IsOpen == true)
    {
        textBox1.Text = "CONNECTED";
    }
    else
    {
        textBox1.Text = "NOT CONNECTED";
    }
}
catch (Exception ex)
{
    MessageBox.Show("Error: " + ex.ToString(), "ERROR");
}

and the other settings are done with the property manager (the only difference is baud is set to 230400; all others are on their default). I can open up COM4 with this (an MSP430) which for all intents and purposes is an identical device. I can open COM5 with Termite, so I know the connection is good). And no, I am not trying to open them at the same time. If you need more information let me know and I can post more.

EDIT: I'm on day three of trying to figure this out and still no luck. I don't really understand why I can access this COM port through a terminal program and not my own when, as near as I can see, there is absolutely no difference. Is there a program that can 'examine' a COM port to see the properties of it (besides Windows manager I mean)? I'm getting pretty frustrated and am sort of at a stand still in my project until I figure this out...

EDIT2: I've found an apparent workaround, but I've yet to get it to work here. Now I get a few different I/O errors, but at least it is motion (not sure if it is progress). I've also learned that this is a .NET bug, which has existed since 2.0. I'd still love any help, but if I figure it out I will report back. Zach's code (the workaround linked above) is shown below:

using System;
using System.IO;
using System.IO.Ports;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;

namespace SerialPortTester
{
    public class SerialPortFixer : IDisposable
    {
        public static void Execute(string portName)
        {
            using (new SerialPortFixer(portName))
            {
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (m_Handle != null)
            {
                m_Handle.Close();
                m_Handle = null;
            }
        }

        #endregion

        #region Implementation

        private const int DcbFlagAbortOnError = 14;
        private const int CommStateRetries = 10;
        private SafeFileHandle m_Handle;

        private SerialPortFixer(string portName)
        {
            const int dwFlagsAndAttributes = 0x40000000;
            const int dwAccess = unchecked((int) 0xC0000000);

            if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase))
            {
                throw new ArgumentException("Invalid Serial Port", "portName");
            }
            SafeFileHandle hFile = CreateFile(@"\\.\" + portName, dwAccess, 0, IntPtr.Zero, 3, dwFlagsAndAttributes,
                                              IntPtr.Zero);
            if (hFile.IsInvalid)
            {
                WinIoError();
            }
            try
            {
                int fileType = GetFileType(hFile);
                if ((fileType != 2) && (fileType != 0))
                {
                     throw new ArgumentException("Invalid Serial Port", "portName");
                }
                m_Handle = hFile;
                InitializeDcb();
            }
            catch
            {
                hFile.Close();
                m_Handle = null;
                throw;
            }
        }

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int FormatMessage(int dwFlags, HandleRef lpSource, int dwMessageId, int dwLanguageId,
                                                StringBuilder lpBuffer, int nSize, IntPtr arguments);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetCommState(SafeFileHandle hFile, ref Dcb lpDcb);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool SetCommState(SafeFileHandle hFile, ref Dcb lpDcb);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool ClearCommError(SafeFileHandle hFile, ref int lpErrors, ref Comstat lpStat);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
                                                        IntPtr securityAttrs, int dwCreationDisposition,
                                                        int dwFlagsAndAttributes, IntPtr hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int GetFileType(SafeFileHandle hFile);

        private void InitializeDcb()
        {
            Dcb dcb = new Dcb();
            GetCommStateNative(ref dcb);
            dcb.Flags &= ~(1u << DcbFlagAbortOnError);
            SetCommStateNative(ref dcb);
        }

        private static string GetMessage(int errorCode)
        {
            StringBuilder lpBuffer = new StringBuilder(0x200);
            if (
                FormatMessage(0x3200, new HandleRef(null, IntPtr.Zero), errorCode, 0, lpBuffer, lpBuffer.Capacity,
                              IntPtr.Zero) != 0)
            {
                return lpBuffer.ToString();
            }
            return "Unknown Error";
        }

        private static int MakeHrFromErrorCode(int errorCode)
        {
            return (int) (0x80070000 | (uint) errorCode);
        }

        private static void WinIoError()
        {
            int errorCode = Marshal.GetLastWin32Error();
            throw new IOException(GetMessage(errorCode), MakeHrFromErrorCode(errorCode));
        }

        private void GetCommStateNative(ref Dcb lpDcb)
        {
            int commErrors = 0;
            Comstat comStat = new Comstat();

            for (int i = 0; i < CommStateRetries; i++)
            {
                if (!ClearCommError(m_Handle, ref commErrors, ref comStat))
                {
                     WinIoError();
                }
                if (GetCommState(m_Handle, ref lpDcb))
                {
                     break;
                }
                if (i == CommStateRetries - 1)
                {
                     WinIoError();
                }
            }
        }

        private void SetCommStateNative(ref Dcb lpDcb)
        {
            int commErrors = 0;
            Comstat comStat = new Comstat();

            for (int i = 0; i < CommStateRetries; i++)
            {
                 if (!ClearCommError(m_Handle, ref commErrors, ref comStat))
                 {
                     WinIoError();
                 }
                 if (SetCommState(m_Handle, ref lpDcb))
                 {
                     break;
                 }
                 if (i == CommStateRetries - 1)
                 {
                     WinIoError();
                 }
            }
        }

        #region Nested type: COMSTAT

        [StructLayout(LayoutKind.Sequential)]
        private struct Comstat
        {
            public readonly uint Flags;
            public readonly uint cbInQue;
            public readonly uint cbOutQue;
        }

        #endregion

        #region Nested type: DCB

        [StructLayout(LayoutKind.Sequential)]
        private struct Dcb
        {
            public readonly uint DCBlength;
            public readonly uint BaudRate;
            public uint Flags;
            public readonly ushort wReserved;
            public readonly ushort XonLim;
            public readonly ushort XoffLim;
            public readonly byte ByteSize;
            public readonly byte Parity;
            public readonly byte StopBits;
            public readonly byte XonChar;
            public readonly byte XoffChar;
            public readonly byte ErrorChar;
            public readonly byte EofChar;
            public readonly byte EvtChar;
            public readonly ushort wReserved1;
        }

        #endregion

        #endregion
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            SerialPortFixer.Execute("COM1");
            using (SerialPort port = new SerialPort("COM1"))
            {
                port.Write("test");
            }
        }
    }
}

EDIT3: Day 6: I'm still plugging away at this. My water rations are low, but still I struggle on. I feel help must surely be on the horizon. Whoever finds this journal bring my remains back to Canada and find Nicole. Tell her I love her.

But seriously, I have no idea what is causing this problem. I'm wondering if it is purely on the embedded side; maybe because it is USB On-The-Go (OTG), or because the device is capable of being a host also. Has anyone run into that problem? It doesn't explain why I can use Termite (a terminal program, for those viewers just joining us) though. I have been trying to find an open source terminal program that a) works and b) see a). As per usual, I will report back if I discover the issue here as I have now found countless forums where it sounds people have had this issue dating back to 2006.

EDIT4: So as per the advice given, I downloaded a port monitoring software application (I got Eltima Serial Port Monitor), and it does look like a baud issue:

Screen capture from Eltima

But strangely no matter what baud I set, it still fails. And also can someone explain what the up/down thing means? I tried googling it, but the keywords are too general. As usual, I will keep reporting back any changes.

Also, for the record, I can connect using Eltima at a baud of 115200 (same as Termite). Unfortunately this does not work in Visual Studio.

EDIT5: Our plot takes a surprise twist. I was monitoring what happens when Termite connects to the COM port in question and BLAM! Termite throws the exact same error as my program, but it ignores it. Genius, right? Sloppy, but it works. Now I need to learn how to ignore IOExceptions. I'll report back when I get it figured out.

EDIT6: So as it turns out it is a baud rate issue, but it goes deeper. I have been using Eltima Serial Port Monitoring software, and it is very intuitive and easy to use. I would recommend it. After some research I have learned that you cannot ignore this exception and still connect to the serial port using .NET's library.

So I have to go deeper into the Win32 API and write my own. I have found a few pages that touch on this, but to be honest I have never done anything like this before, so it may be a while before I report back, but I will definitely figure this out and get back to everyone. There are way too many who suffer from this problem.

I have found quite a few forums and websites where I can see the exact same symptoms, but nobody has really done much besides say 'Yeah, .NET sucks'. I plan on writing a full static library class and then publish either on my website, here and wherever else I can. Hopefully .NET will take notice (this bug has existed since 2.0).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tmwoods
  • 2,353
  • 7
  • 28
  • 55
  • What settings are you using to connect? Can we please see the code? – Paul Ruane Feb 14 '13 at 22:35
  • I just added my code to the original post. I don't know how to add code in the comments... – tmwoods Feb 14 '13 at 22:41
  • 1
    My only guess it that your properties are somehow wrong. Perhaps try lowering the baud rate down to 9600. – Paul Ruane Feb 14 '13 at 22:46
  • No dice :( Same error message. – tmwoods Feb 14 '13 at 22:50
  • Does the error message point to anything in particular? I don't have experience with this and I'm not too sure what I am looking at. – tmwoods Feb 14 '13 at 22:51
  • 1
    If you go to *Control Panel* → *Administrative Tools* → *Computer Management* → *Device Manager* → *Ports*, find the virtual port and open its properties you should be able to see its setting under the *Port Settings* tab. I would try matching these settings. – Paul Ruane Feb 14 '13 at 22:53
  • That is how I started and everything is identical. – tmwoods Feb 14 '13 at 22:54
  • 1
    Um. Have you tried switching it off and back on again? – Paul Ruane Feb 14 '13 at 22:55
  • I've tried all the 'obvious' methods; I think this goes deeper and was hoping the issue was somehow shown in the IO code. I'm going to be in motion for a few hours and may not check back straight away. Thanks for trying though :) – tmwoods Feb 14 '13 at 22:58
  • 1
    Hi tmwoods, Can you please post Zach's code into your question? Thanks, it's customary in StackOverflow (and Superuser) questions/answers to include a summary of the contents of a link or the highlights that specifically answer the question. The goal of SE sites is to become a resource of knowledge, of answers, for years to come. With a link-only reference to an answer, people must dig through another resource to locate an answer he/she might not be sure about. Most importantly, if your link were to ever break, your answer is useless for anyone who visits this page in the future. Good luck! – Jeremy Thompson Feb 19 '13 at 00:05
  • Of course I will. Thanks for the tip :) – tmwoods Feb 19 '13 at 00:21
  • 1
    Just wondering... Have you tried running VS in elevated mode (e.g. as Administrator)? – atlaste Feb 19 '13 at 19:46
  • I have not; but I definitely will try it the next time I have a chance :) EDIT: I ran to the lab to try it. No luck :( – tmwoods Feb 19 '13 at 19:48
  • 1
    3 things i'd try: 1. give admin permissions to the exe you are running, not only to the visual studio 2. assign a different port number, try use COM1-COM4, some devices are designed (not well) to work only with the 'low' com ports. 3. try using this little open source app to connect https://msmvps.com/blogs/coad/archive/2005/03/23/SerialPort-_2800_RS_2D00_232-Serial-COM-Port_2900_-in-C_2300_-.NET.aspx – Adiel Yaacov Feb 21 '13 at 09:52
  • I definitely will; I have tried the second one (changing port numbers) but I'm pretty excited to try your open source app. Once I get to the lab later today (hopefully) I will report back. – tmwoods Feb 21 '13 at 16:28
  • Alas, no luck. Throws the same error. So far Termite is the only program that will open it (tried Realterm and PuTTy, as well as three third-party apps, this open source and my own). Bummer. – tmwoods Feb 21 '13 at 18:42
  • I've seen similar issues with a prolific based USB serial port where any .NET code would fail to open it if it's been used by any native Win32 program previously. The only solution I had was to re plug the USB port. – Deanna Feb 28 '13 at 14:30
  • Do you mean physically re-plug it? Because that hasn't worked for me. Or is that some lingo that I am too new to understand? (I am hoping for the latter)... – tmwoods Feb 28 '13 at 14:53
  • @tmwoods Yes, as in plug in again. – Deanna Mar 11 '13 at 12:07
  • @Deanna: In that case, yes, I have tried that. I've also tried running this in safe mode, through a linux environment and a few others to make sure it isn't being used in a secondary program. No luck still :( – tmwoods Mar 11 '13 at 13:53
  • @tmwoods hey! i am facing this same problem as you!!! i am using the STM32F2xx to emulate it as VCP, and I have same problem as you, cant open in .NET but opens in terminal, may I know how did u solve the firmware part? – Tim Oct 20 '14 at 05:31
  • @Tim Hi Tim! It was that we were using an older library for the TI chip (I see you are using ST). We simply replaced it with a newer library and it worked. I know that isn't the news you need to fix your problem. I will try to take a look at the two libraries to see any differences but honestly it's been a while since I looked at this code and it's a bit fuzzy as to what actually changed. Sorry I couldn't be more help! – tmwoods Oct 20 '14 at 16:10
  • @tmwoods hey! i managed to trace the problem in my firmware, when the PC open the serial port communication, it will ask for the firmware VCP information for baudrate, parity, and data bit length, in my firmware the callback for VCP communication doesnt contain info about those things, and when PC tries to open it causes problem because the firmware VCP is not giving any of those information which causes the error! i think this is the problem, as i fixed it till now i dont have the problem! you mind to check ur firmware if the same behavior exists between ur old and new firmware? to confirm? – Tim Oct 31 '14 at 07:35
  • @Tim I declared all that right in the firmware (i.e. it is static, not dynamic). I set those values permanently and just did the same thing in my Visual Studio code (I was doing Windows Forms). I did this with both (old & new) versions of my firmware. – tmwoods Oct 31 '14 at 16:09
  • @tmwoods then i not sure! haha because in my firmware (using STM32F2xx) there is a callback for the USB VCP (CDC_Control_FS -> cmd == CDC_GET_LINE_CODING), here i dint declare my baudrate, parity and data bit! when the PC open the serial port, the firmware will make a callback into this functioon, to check for the params! I guess my problem and yours is different? – Tim Nov 04 '14 at 00:23
  • Hi @tmwoods , Did you try to Run `GC.Collect()` before getting information from port? – jmvtrinidad Mar 08 '17 at 09:20

5 Answers5

8

This comes from the serial port driver; it is unhappy about one of the settings. With baudrate being a good candidate, drivers tend to allow only up to 115200. Albeit that this should not be a restriction when this is a dedicated CAN bus product.

The best way to tackle this is by using Sysinternals' Portmon utility; you can see what is being sent to the driver. Observe it for Terminate first; that's your known-to-work baseline. Then tinker with SerialPort properties until the initialization commands, as you see them in PortMon, sent by your program matches Termite's. Just the values, not the order. If that doesn't pan out either then take it to the parking lot and back over it with your car several times and buy another brand.


Update: it certainly looks like a baudrate problem. That's an issue in .NET; it is not going to ignore the driver's error return code like your terminal emulator programs do. The actual value should not matter since you are talking to an emulated serial port. There is however a possible issue with the CAN bus speed; rates are variable and it isn't clear to me how they are negotiated. This tended to be done with DIP switches in the olden days, and it may well be that the driver wants you to specify the speed through the baudrate setting. There ought to be something about it on the box or in the manual. Typical speeds are 40, 250 or 500 kbit/s. The manufacturer certainly would know; give them a call.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I am out of the office for the evening but when I get in tomorrow I will give it a go :) – tmwoods Feb 15 '13 at 02:03
  • Can you recommend a more up-to-date port monitoring software package? The only version I can find of this isn't compatible with Windows 7 (it would appear the last time they updated this software was for Win98). It says it is copyrighted to 2012, but I can't get it to connect and all the help files are either missing or explained for a Win98 environment. – tmwoods Feb 21 '13 at 21:58
  • I tried Eltima Serial Port Monitor, see the main body of the question above. – tmwoods Feb 21 '13 at 22:13
  • Erm, are we talking about SysInternals' PortMon utility? It most certainly runs on Windows 7. Do note that you have to run it elevated, right-click + Run as Administrator. – Hans Passant Feb 21 '13 at 23:14
  • I already deleted it but I'm sure that would have fixed the problem. Eltima works great and I see where the invalid parameter is occuring, with both Termite and my program. Now I am trying to figure out how to safely ignore the exception *and* open the port. I don't want to mark the question as solved until I do this. Thanks, any more input is appreciated :) – tmwoods Feb 21 '13 at 23:24
  • So I wrote a program to run test connecting with baud rates between 1200 and 500,000 bps (with a suitable pause between each). It took a while to run the whole thing but there was no luck (I paused it a few times to test the connction with Termite just in case). This thread explains what it is that I actually think is the issue. So what I'm going to do is try to write some custom functions using the Win32 API and then release them because too many people suffer from this. I'll report back probably within a week once I either fail or accomplish this. – tmwoods Feb 23 '13 at 19:44
  • The BaudRate for comports should be almost always set to 9600, try that out please – Max Feb 24 '13 at 22:14
6

I faced a similar problem as reported in this thread, but I managed to solve the problem!

I am using STM32F2xx for the VCP!

And indeed it was my firmware problem. I forgot to include serial port settings in my USB callback!

The process of connecting a serial port from PC and firmware:

  1. When a PC opens a serial port communication, the PC will send some command into the "configuration endpoint"
  2. In the firmware, it would have a callback and the firmware will provide all the USB information (they call it a USB descriptor)
  3. USB information is the configuration of each endpoint, (for example, latency, data size transmission, and type of USB - high speed or low speed)
  4. Once the firmware has completed sending all the information, the PC will acknowledge and USB communication is successfully opened
  5. Then, the PC will send a command to get the serial port settings from the firmware
  6. Serial port settings are baudrate, data parity, and bit length.
  7. In firmware, it should reply the serial port settings back to PC (my mistake occurs here; I didn’t not send any serial port settings back to the PC)
  8. If successful, PC will start the serial port communication!
  9. If failed, PC will give an open serial port error (but, do note that this error sometimes is bypassed)

In STM32 firmware code:

static int8_t CDC_Control_FS (uint8_t cmd, uint8_t* pbuf, uint16_t length)
{
    switch (cmd) {
       case CDC_GET_LINE_CODING:
        {
            // I was missing this part
            uint32_t baudrate = 9600;
            pbuf[0] = (uint8_t)(baudrate);
            pbuf[1] = (uint8_t)(baudrate >> 8);
            pbuf[2] = (uint8_t)(baudrate >> 16);
            pbuf[3] = (uint8_t)(baudrate >> 24);
            pbuf[4] = 0;
            pbuf[5] = 0;
            pbuf[6] = 8;
            break;
        }:
....
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tim
  • 3,755
  • 3
  • 36
  • 57
  • 1
    This also did the trick for me when I was running into a similar issue. Specifically, whenever the device would disconnect and reconnect, it would appear in device manager and be recognized as a USB-Serial COM port, but would be unusable by any software and .NET would throw the "A device attached to the system is not functioning" exception. Issue was fixed by ensuring that the USBUART device (Cypress PSoC5LP) firmware was properly setting the active port and re-initializing the CDC interface (sending over the port settings) whenever a change in USB Configuration was detected. – K_Trenholm Oct 05 '21 at 17:13
5

I ran into the same situation. I am trying to connect serial communication to my 3G USB Dongle (Huawei E303F) at /dev/ttyUSB0. I use Mono in Raspbian (Raspberry Pi 2). On my development PC and macOS, my program runs fine. But when I deploy it into Raspbian, I got the IOException Broken Pipe error on Serial.Open().

It took me three days of debugging, and I tried all possible solutions. Finally I found that I have to set...

serialPort.DtrEnable = true;
serialPort.RtsEnable = true;

Before calling .Open().

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tanutapi
  • 1,083
  • 9
  • 7
1

And so our thrilling tale comes to a close. It was firmware the whole time (i.e. the code on the embedded device). We changed up a few functions and essentially poked around, chopped, added and altogether cleaned up the code and voila, the code is working. This pic sums it up pretty well. Curse you firmware!!

However, the bug described in my (lengthy) question still persists for many people and I know there are lots of people out there who still have it. All I can say is good luck and quadruple check your firmware (apparently triple checking it isn't enough these days).

tmwoods
  • 2,353
  • 7
  • 28
  • 55
-1

I had the same problem and setting the baud rate to 1 fixed it!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131