user27911: I am trying to write a little program that needs to detect Arduino ports automatically. I have tried many methods but haven't found any reliable way.
I can see that the Arduino IDE can detect it, by showing /dev/cu.xyz (Arduino Uno) or COM6 (Arduino Uno) etc.
How could I detect if the device connected to the serial port is an Arduino or not? Preferably without pre-flashing the Arduino with specific firmware.
I found this Python version, which uses serial.tools.list_ports.comports() here,  but it didn't work when I tested it on Windows and MacOS.
The best I can get is by using go-serial's USBManufacturer, I can get 'Arduino (www.arduino.cc)'.
I also found this post, about the same question, but without any solution.

user27911: Here is a solution:
On Windows, Arduino IDE comes with a tool named listComPorts.exe, which will list all COM ports and their USB info like VID & PID. There is also a file named arduino.inf in drivers folder contains all known device types and their VID&PID etc, and we can look for the connected device.
On *nix, the IDE take advantage of libusb and no such a tool, and it's easy to find a tool to list usb info on these platforms.
This go package mentioned in the question is a good choice, which also uses libusb and could build on all platforms. (On windows, you have to install mingw to build the C code, cygwin & msys don't work, I tried TDM-GCC and it works. You also have to add LDFLAGS: -lsetupapi (in serial.go) to successfully link the code.) After list all serial ports, you can search for 'Arduino' to determine the port. 'CH340' is also a keyword to search as many modified boards use this chipset.
New version of the IDE don't have listComPorts.exe, they all uses libusb. I find this tool from version 1.6.5.

user71819: Quite late for the party! Just leave it here for anyone looking for a solution on windows.
The proper way is to read the device list from registry.
First location is

HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM


A better location is here

HKLM\SYSTEM\CurrentControlSet\Services\usbser\Enum


You have the list of Arduino devices there. Mine is Arduino Leonardo. Genuine Arduino UNO uses the similar chip and it should be in there as well.
To get the port, just copy the first device (index 0) and plug into this location

HKLM\SYSTEM\CurrentControlSet\Enum

So it is like this

HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_2341&PID_8036&MI_00\9&88cb209&0&0000



So you have friendly name and port number of the serial device. Then just write some code to read these registry locations.
In C
#include <windows.h>
#include <wchar.h>
#define MAX_VALUE_LENGTH 255

void getUSBSerialList()
{

    HKEY hKey;
    //check if we can read registry
    INT dwRegOPenKey = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\usbser\\Enum"), 0, KEY_READ, &hKey);
    if(dwRegOPenKey != ERROR_SUCCESS) return 0;


    DWORD type=REG_DWORD, size=0, USBser_count = 0; // a place for RegQueryValueEx to write back to
    DWORD dwRes;

    //read count of usb devices
    dwRes = RegQueryValueExW(hKey, _T("Count"), NULL, (LPDWORD) &type, (LPBYTE) &USBser_count, &size);
    if (dwRes) return 0;

    if (USBser_count) 
    {
        for (INT i=0, dwRes = ERROR_SUCCESS; i<USBser_count; i++) 
        { 
            WCHAR achValue[MAX_VALUE_LENGTH]; 
            WCHAR buffer[MAX_VALUE_LENGTH];
            ZeroMemory(buffer, sizeof WCHAR  * MAX_VALUE_LENGTH);

            swprintf(achValue, L"%d",i); // read value name "0", "1"...
            dwRes = RegQueryValueExW(hKey, achValue, NULL, (LPDWORD) &type, (LPBYTE) &buffer, &size);
            
            // Plug that value into "SYSTEM\\CurrentControlSet\\Enum\\"
            HKEY hKey2;
            swprintf(achValue, L"SYSTEM\\CurrentControlSet\\Enum\\%s",buffer);
                
            INT dwRegOPenKey2 = RegOpenKeyExW(HKEY_LOCAL_MACHINE, achValue, 0, KEY_READ, &hKey2);
            if (dwRegOPenKey2) return 0; // check if this key exists
            
            // From here read friendly name
            dwRes = RegQueryValueExW(hKey2, _T("FriendlyName"), NULL, (LPDWORD) &type, (LPBYTE) &buffer, &size);
            if (dwRes) return 0;
            
            // or read COM port
            swprintf(achValue, L"%s\\Device Parameters", achValue);
            dwRegOPenKey2 = RegOpenKeyExW(HKEY_LOCAL_MACHINE, achValue, 0, KEY_READ, &hKey2);
            if (dwRegOPenKey2) return 0;

            dwRes = RegQueryValueExW(hKey2, _T("PortName"), NULL, (LPDWORD) &type, (LPBYTE) &buffer, &size);
            if (dwRes) return 0;
        }
    }
}

In C#, just use Registry object to do so
int usbser_count = (int)Registry.GetValue("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\usbser\\Enum", "Count", -1);

I haven't done it in C# but I think it's easier than in C.
What if yours is arduino clone that use CH340 instead of atmega16u2, it won't appear in the registry location above. It will show up here instead:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CH341SER_A64\Enum

You can do the same for other type of USB serial devices like CP210x (on some ESP32 dev boards)

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\silabser\Enum

Or even get a list of WinUSB devices (such as ST-Link v2)

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WINUSB\Enum


27911: <p>I am trying to write a little program that needs to detect Arduino ports automatically. I have tried many methods but haven't found any reliable way.</p>

<p>I can see that the Arduino IDE can detect it, by showing <code>/dev/cu.xyz (Arduino Uno)</code> or <code>COM6 (Arduino Uno)</code> etc.</p>

<p>How could I detect if the device connected to the serial port is an Arduino or not? Preferably without pre-flashing the Arduino with specific firmware.</p>

<p>I found this Python version, which uses <code>serial.tools.list_ports.comports()</code> <a href="https://stackoverflow.com/questions/24214643/python-to-automatically-select-serial-ports-for-arduino">here</a>,  but it didn't work when I tested it on Windows and MacOS.</p>

<p>The best I can get is by using <a href="https://godoc.org/github.com/mikepb/go-serial#Info" rel="noreferrer">go-serial</a>'s <code>USBManufacturer</code>, I can get 'Arduino (www.arduino.cc)'.</p>

<p>I also found <a href="https://forum.arduino.cc/index.php?topic=115517.0" rel="noreferrer">this post</a>, about the same question, but without any solution.</p>
 27911: <p>Here is a solution:</p>

<p>On Windows, Arduino IDE comes with a tool named <strong>listComPorts.exe</strong>, which will list all COM ports and their USB info like VID &amp; PID. There is also a file named <strong>arduino.inf</strong> in drivers folder contains all known device types and their VID&amp;PID etc, and we can look for the connected device.</p>

<p>On *nix, the IDE take advantage of libusb and no such a tool, and it's easy to find a tool to list usb info on these platforms.</p>

<p><a href="https://godoc.org/github.com/mikepb/go-serial" rel="nofollow noreferrer">This go package</a> mentioned in the question is a good choice, which also uses libusb and could build on all platforms. (On windows, you have to install mingw to build the C code, cygwin &amp; msys don't work, I tried TDM-GCC and it works. You also have to add <code>LDFLAGS: -lsetupapi</code> (in serial.go) to successfully link the code.) After list all serial ports, you can search for 'Arduino' to determine the port. 'CH340' is also a keyword to search as many modified boards use this chipset.</p>

<p>New version of the IDE don't have <strong>listComPorts.exe</strong>, they all uses libusb. I find this tool from version 1.6.5.</p>
 71819: <p>Quite late for the party! Just leave it here for anyone looking for a solution on windows.</p>
<p>The proper way is to read the device list from registry.</p>
<p>First location is</p>
<blockquote>
<p><em><strong>HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM</strong></em></p>
</blockquote>
<p><a href="https://i.stack.imgur.com/YvlZA.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/YvlZA.png" alt="HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM" /></a></p>
<p>A better location is here</p>
<blockquote>
<p><em><strong>HKLM\SYSTEM\CurrentControlSet\Services\usbser\Enum</strong></em></p>
</blockquote>
<p><a href="https://i.stack.imgur.com/IAHaA.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/IAHaA.png" alt="HKLM\SYSTEM\CurrentControlSet\Services\usbser\Enum" /></a></p>
<p>You have the list of Arduino devices there. Mine is Arduino Leonardo. Genuine Arduino UNO uses the similar chip and it should be in there as well.</p>
<p>To get the port, just copy the first device (index 0) and plug into this location</p>
<blockquote>
<p><em><strong>HKLM\SYSTEM\CurrentControlSet\Enum</strong></em></p>
</blockquote>
<p>So it is like this</p>
<blockquote>
<p><em><strong>HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_2341&amp;PID_8036&amp;MI_00\9&amp;88cb209&amp;0&amp;0000</strong></em></p>
</blockquote>
<p><a href="https://i.stack.imgur.com/s2b3C.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/s2b3C.png" alt="HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_2341&amp;PID_8036&amp;MI_00" /></a></p>
<p><a href="https://i.stack.imgur.com/jmiii.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/jmiii.png" alt="HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_2341&amp;PID_8036&amp;MI_00\ --- Device parameters" /></a></p>
<p>So you have friendly name and port number of the serial device. Then just write some code to read these registry locations.</p>
<p>In C</p>
<pre><code>#include &lt;windows.h&gt;
#include &lt;wchar.h&gt;
#define MAX_VALUE_LENGTH 255

void getUSBSerialList()
{

    HKEY hKey;
    //check if we can read registry
    INT dwRegOPenKey = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(&quot;SYSTEM\\CurrentControlSet\\Services\\usbser\\Enum&quot;), 0, KEY_READ, &amp;hKey);
    if(dwRegOPenKey != ERROR_SUCCESS) return 0;


    DWORD type=REG_DWORD, size=0, USBser_count = 0; // a place for RegQueryValueEx to write back to
    DWORD dwRes;

    //read count of usb devices
    dwRes = RegQueryValueExW(hKey, _T(&quot;Count&quot;), NULL, (LPDWORD) &amp;type, (LPBYTE) &amp;USBser_count, &amp;size);
    if (dwRes) return 0;

    if (USBser_count) 
    {
        for (INT i=0, dwRes = ERROR_SUCCESS; i&lt;USBser_count; i++) 
        { 
            WCHAR achValue[MAX_VALUE_LENGTH]; 
            WCHAR buffer[MAX_VALUE_LENGTH];
            ZeroMemory(buffer, sizeof WCHAR  * MAX_VALUE_LENGTH);

            swprintf(achValue, L&quot;%d&quot;,i); // read value name &quot;0&quot;, &quot;1&quot;...
            dwRes = RegQueryValueExW(hKey, achValue, NULL, (LPDWORD) &amp;type, (LPBYTE) &amp;buffer, &amp;size);
            
            // Plug that value into &quot;SYSTEM\\CurrentControlSet\\Enum\\&quot;
            HKEY hKey2;
            swprintf(achValue, L&quot;SYSTEM\\CurrentControlSet\\Enum\\%s&quot;,buffer);
                
            INT dwRegOPenKey2 = RegOpenKeyExW(HKEY_LOCAL_MACHINE, achValue, 0, KEY_READ, &amp;hKey2);
            if (dwRegOPenKey2) return 0; // check if this key exists
            
            // From here read friendly name
            dwRes = RegQueryValueExW(hKey2, _T(&quot;FriendlyName&quot;), NULL, (LPDWORD) &amp;type, (LPBYTE) &amp;buffer, &amp;size);
            if (dwRes) return 0;
            
            // or read COM port
            swprintf(achValue, L&quot;%s\\Device Parameters&quot;, achValue);
            dwRegOPenKey2 = RegOpenKeyExW(HKEY_LOCAL_MACHINE, achValue, 0, KEY_READ, &amp;hKey2);
            if (dwRegOPenKey2) return 0;

            dwRes = RegQueryValueExW(hKey2, _T(&quot;PortName&quot;), NULL, (LPDWORD) &amp;type, (LPBYTE) &amp;buffer, &amp;size);
            if (dwRes) return 0;
        }
    }
}
</code></pre>
<p>In C#, just use Registry object to do so</p>
<pre><code>int usbser_count = (int)Registry.GetValue(&quot;HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\usbser\\Enum&quot;, &quot;Count&quot;, -1);
</code></pre>
<p>I haven't done it in C# but I think it's easier than in C.</p>
<p>What if yours is arduino clone that use CH340 instead of atmega16u2, it won't appear in the registry location above. It will show up here instead:</p>
<blockquote>
<p><em><strong>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CH341SER_A64\Enum</strong></em></p>
</blockquote>
<p>You can do the same for other type of USB serial devices like CP210x (on some ESP32 dev boards)</p>
<blockquote>
<p><em><strong>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\silabser\Enum</strong></em></p>
</blockquote>
<p>Or even get a list of WinUSB devices (such as ST-Link v2)</p>
<blockquote>
<p><em><strong>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WINUSB\Enum</strong></em></p>
</blockquote>
