Introduction
I needed to write an application that interacted with an Arduino board via its COM port. The lazy way is to display a list of all known
comports and get the user to select the correct one. It's about time for a better way.
Background
Most USB serial port devices have unique hardware ID's, so as you plug in more serial devices, each device is then allocated a unique COM port number.
My approach is to compile an array of all COM port numbers that were historically allocated to a particular VID/PID combination.
From there it is simple to compare the list of COM ports open now to the list of comports that were allocated to the VID/PID.
The intersection of the 2 lists is the COM port desired.
How to find the VID and PID numbers
The information you need is located in the Device Manager, sometimes in more than one place. Note that the device must be plugged in to make it visible.
Open the device manager and expand the list of COM ports. Here's what I see on my machine when the Arduino board is plugged in.
Select the port of interest, in this case COM8, right click and select Properties. A dialog box pops up, select the Details tab and Hardware ID's property. The VID and PID are displayed.
Make a note of the 4 digit numbers of interest, in this case VID is 0403, PID is 6001.
Using the code
My initial attempt used WMI and Win32 classes, but I was not able to find the information needed. Instead I turned to the Registry.
I know that this is not a 100% .NET solution, but will work for the vast majority of
.NET users out there.
The function below drills down though the relevant part of the registry and filters out values with the desired VID and PID values.
Note the use of Regular Expressions. That's because Registry key formats are inconsistent. I am looking for a key in the format VID_XXXX<separator>PID_XXXX, where XXXX are 4 hex digits and <separator> is (any) single character. By examining the registry via RegEdit you'll see uppercase, lowercase or a mixture of both. <separator> may be a '+' or a '&'.
Regular Expressions makes the comparison much simpler and reliable.
Caveat Emptor
This method returns a list of ALL serial devices with that VID/PID combination currently plugged in. That will be a problem if you have more than 1 device from the same manufacturer in use at the same time.
List<string> ComPortNames(String VID, String PID)
{
String pattern = String.Format("^VID_{0}.PID_{1}", VID,PID);
Regex _rx = new Regex(pattern, RegexOptions.IgnoreCase);
List<string> comports = new List<string>();
RegistryKey rk1 = Registry.LocalMachine;
RegistryKey rk2 = rk1.OpenSubKey("SYSTEM\\CurrentControlSet\\Enum");
foreach (String s3 in rk2.GetSubKeyNames())
{
RegistryKey rk3 = rk2.OpenSubKey(s3);
foreach (String s in rk3.GetSubKeyNames())
{
if (_rx.Match(s).Success)
{
RegistryKey rk4 = rk3.OpenSubKey(s);
foreach (String s2 in rk4.GetSubKeyNames())
{
RegistryKey rk5 = rk4.OpenSubKey(s2);
RegistryKey rk6 = rk5.OpenSubKey("Device Parameters");
comports.Add((string)rk6.GetValue("PortName"));
}
}
}
}
return comports;
}
A simple test takes the compiled list and compares it against COM ports open at this time.
static void Main(string[] args)
{
List<string> names = ComPortNames("0403", "6001");
if (names.Count > 0)
{
foreach (String s in SerialPort.GetPortNames())
{
if (names.Contains(s))
Console.WriteLine("My Arduino port is " + s);
}
}
else
Console.WriteLine("No COM ports found");
}
History
- 3/17/2012: Original version.