listComPorts – Windows command-line tool for USB-to-serial

Did you know each Arduino has a unique serial number in its USB interface that you can use to distinguish one Arduino from another? If you deal with multiple Arduinos, knowing exactly which one is plugged into your computer can be a real time-saver. But actually getting at this serial number and mapping it to COM ports can be challenging.

For Windows computers, here’s “listComPorts”, implemented both in GCC C code and in VBScript, both available from my usbSearch github repository.

It gives the COM port number, the manufacturer name, the USB Vendor ID and Product ID (VID & PID) and the serial number. The result is a concatenation of the VID, PID, and serial number that Windows calls a “PnPDeviceID”. You can use the whole thing, or pick off the parts you need using simple string processing libraries. In the example above, the Arduino Diecimila has a VID/PID pair of 0403/6001 and the Teensy has a VID/PID of 16C0/0483.

Arduino Serial Numbers

In the above example, the Arduino Diecimila (which has an FTDI chip) has the serial number with a “+”-sign after the PID. So, the serial number is “A6004CCF”. The Teensy can have a programmable serial number and by default it’s set to “12345”.

With Arduino UNOs and future Arduinos, the Arduino team has their own VID/PID pair, which let’s them assign different PIDs for different classes of Arduinos. The UNO also has a much longer unique serial number than older Arduinos using the FTDI chip.

Here’s an example with two Arduino UNOs:

The serial numbers are separated by the VID/PID pair with a backslash. So my two UNOs have the serial numbers “64936333936351408161” and “6493234373835191F1F1”. Using this info, I can be sure exactly which Arduino I’m dealing with.

How it works

The C and the VBS versions both utilize the WMI infrastructure that’s been around since Windows 2000 to query the machine about its configured PnP devices. The WMI is a huge data structure of just about any information in Windows. Except, it seems, good information about COM ports. While there is a “Win32_SerialPort” table in WMI, that only contains information about hardware serial ports, not USB-to-serial adapters. Instead, these two tools look at the “Win32_PnPEntity” table. While this table does list USB-to-serial adapters, it does not contain a proper mapping of the adapter’s USB or PnP ID to COM port. Instead, these tools do a string search on the “Caption” field for the string “(COMn)” where “n” is a number. It’s an incredible hack but seems to work.

Other operating systems

On Mac OS X and pre-UNO Arduinos, knowing exactly which Arduino you were dealing with was easy because the FTDI driver put its serial number as part of the name of the serial port. For example, the port name created by the FTDI driver was “/dev/tty.usbserial-A6004ccf” and the serial number was “A6004ccf”.

With the UNO, the Arduino team used a reprogrammable ATmega8/16u USB chip using standard CDC USB-to-serial interface, which all OSes support natively. Unfortunately, on Mac OS X, the OS’s CDC driver creates a serial port for the UNO based on USB port location, not Arduino serial number.

Thus where you plug in the UNO into determines its serial port name. For instance, on my Mac, plugging in an UNO with serial number “64936333936351408161” gives a serial port with the name of either “/dev/tty.usbmodemfd131” or “/dev/tty.usbmodemfa141” depending on which USB jack is used. Even if you plug in a different UNO in the same jack, you’ll get the same serial port name. This is useful for some applications, but not if you need to know exactly which UNO you’re dealing with.

For Mac OS X, I use a tool I wrote called listArduinos.pl. It tries to determine the serial port names of every Arduino Uno. It’s out looks like:

bokbok% ./listArduinos.pl         
Finding Arduino UNOs...
/dev/tty.usbmodemfd131 - 0x2341/0x0001 - 6493234373835191F1F1
/dev/tty.usbmodemfa141 - 0x2341/0x0001 - 64936333936351400000
Found 2 Arduino UNOs
bokbok% 

13 Replies to “listComPorts – Windows command-line tool for USB-to-serial”

  1. And what about PowerShell:

    Get-CimInstance win32_PnPEntity | ? name -match “com\d”

    Regards

    R.)

  2. To make it work in Windows 10:
    in disphelper.c, function InternalInvokeV
    hr = VariantChangeType(pvResult, pvResult, 16 , returnType);
    if (FAILED(hr))
    {
    if(hr == 0x80020005)
    hr = S_OK;//If typeMisMatch occurs, 80020005, don’t bother.
    VariantClear(pvResult);
    }
    it seems to occur in an empty area.
    I built it with VisualStudio 2017, with small changes to the includes.
    It works on my brand new HP windows 10, latest microsoft update. (oct 2018).
    \Wiken :)

  3. The type-error is caused by a devicename with a Null value:
    Improved ListComPorts.vbs:

    '
    ' listComPorts -- List all COM, even USB-to-serial-based onesports,
    '                 along with other info about them
    '
    ' Execute on the command line with:
    '   cscript.exe //nologo listComPorts.vbs
    '
    ' http://github.com/todbot/usbSearch
    '
    ' 2012, Tod E. Kurt, https://todbot.com/blog/
    ' 2017  N. Teering: Fixed devicename with Null value
    '
    ' core idea stolen from
    ' http://collectns.blogspot.com/2011/11/vbscript-for-detecting-usb-serial-com.html
    ' And this is fun, if not particularly useful:
    ' http://henryranch.net/software/jwmi-query-windows-wmi-from-java/
    ' 
    
    Set portList = GetComPorts()
    
    portnames = portList.Keys
    for each pname in portnames
        Set portinfo = portList.item(pname)
        wscript.echo pname & " - " & _
               portinfo.Manufacturer & " - " & _
               portinfo.PNPDeviceID & " - " & _
               portinfo.Name
    Next
    
    '
    ' For all the keys in an entity, see
    'http://msdn.microsoft.com/en-us/library/windows/desktop/aa394353(v=vs.85).aspx
    '
    Function GetComPorts()
        set portList = CreateObject("Scripting.Dictionary")
    
        strComputer = "."
        set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
        set colItems = objWMIService.ExecQuery ("Select * from Win32_PnPEntity")
        for each objItem in colItems
            If Not IsNull(objItem.Name) Then
            	set objRgx = CreateObject("vbScript.RegExp")
            	objRgx.Pattern = "COM[0-9]+"
    	        Set objRegMatches = objRgx.Execute(objItem.Name)
    	        if objRegMatches.Count = 1 Then  portList.Add objRegMatches.Item(0).Value, objItem 
    	    End if
        Next
        set GetComPorts = portList
    End Function
    
  4. I have the same error. Is there anybody who can help? :)
    PS. Windows 7 64bit => work well, but win10 => work bad ;)

  5. Hi,
    thank you very much for your little tool, it is very handy for a little automated batch script i am using to write code to a little microcontroller. while the script works great, i found a little bug on the software on my desktop pc.
    i have an emulated serial port (ethernet to rs232) in my system, causing the application to crash.

    The console output is:
    COM7 – Lantronix – ROOT\PORTS\0000

    ??? no summary of detected ports ???

    The Errormessage is:
    Member: .Name
    Function: GetValue
    Error IN: internalInvokeV
    Error: Typenkonflict (type error)
    Code: 80020005
    Source: Application

    maybe you could check it out

  6. Hi Remuar,
    Thanks! All these tools just run and exit, like a normal script. They’re meant to be used in a command window or in a script. They don’t know if they’re being run from double-clicking (which is I assume what you’re doing). If you know of a way to detect this, I’ll gladly add it to usbSearch.exe.

  7. Hi Tod,

    Thank´s for this nice tool… very often useful and avoiding the win/hardware way

    one little wish would be to keep the console open after run so you don´t need to open a cmd prompt each time.
    But for sure it´s a detail!

    kind regards

  8. Hi Marcos,
    I actually don’t know too much about Windows internals, but I think one should be able to change the search strings used by the various WMI search strings in the usbSearch repo to get bus connectedness.

    Also, if the device has two internal USB devices on it, there may be some identifier that lets you pair them up, just by looking at the PnP entries or even serial numbers.

  9. Hello Tod,

    Thanks so much for your code.

    I’m working on a device that uses a internal hub chip to connect two usb devices. My application must connect to both devices to work correctly. My problem is, if I have two devices connected to my pc, I’ll have 4 comports. I cant figure out how can I diferenciate the comports pairs. I belive that I need list the current hubs and so find bellow this hub (node) looking for devices, making the connection with the pair found directly bellow.

    Is possible change your code in order to get this functionality? Or, do you have some tip how can I do this job?

    Thanks so much,
    Marcos

Leave a Reply

Your email address will not be published. Required fields are marked *