FTDI Community

Please login or register.

Login with username, password and session length.
Advanced Search  

News:

Welcome to the FTDI Community!

Please read our Welcome Note

Technical Support enquires
please contact the team
@ FTDI Support


New Bridgetek Community is now open

Please note that we have created the Bridgetek Community to discuss all Bridgetek products e.g. EVE, MCU.

Please follow this link and create a new user account to get started.

Bridgetek Community

Author Topic: Can I assign my UART Serial Adapter Cables text strings that a PC can read?  (Read 14979 times)

earnie

  • Newbie
  • *
  • Posts: 7
    • View Profile

I have several UART Serial Adapter Cables of the following types:

https://ftdichip.com/products/c232hd-ddhsp-0/
https://ftdichip.com/products/ttl-234x-3v3-we/

I would like to make it easier to identify all the different cables from within a home-made Windows application (it's written in C#, but it's capable of calling DLL-files). Is it somehow possible to assign the cables descriptive text strings such as "Mike's 3 MBaud cable", "John's 12 MBaud cable with power switch", "Kevin's UART cable with Tx/Rx tied together", etc in some non-volatile storage inside the cables? I read in the following manual https://ftdichip.com/wp-content/uploads/2020/08/D2XX_Programmers_GuideFT_000071.pdf that many settings can be modified, but I don't have enough USB knowledge to know which settings are safe to modify. It would be a bonus if the text descriptions also would show up in the Device Manager somewhere, either directly in the tree or if you'd go in and look under properties, but it's not a requirement. Typically, I use the standard VCP-drivers, but if there's a very specific reason to do so, I guess I could switch to a different driver and make my home-made Windows application install that driver automatically if it's not present on the computer (I don't like having to supply driver separately).
« Last Edit: February 19, 2021, 12:26:07 PM by earnie »
Logged

earnie

  • Newbie
  • *
  • Posts: 7
    • View Profile

I guess a workaround would be to use the serial number (see code below) and then have a lookup table for the text description in a file, but if someone has a suggestion for a solution without the need for a lookup table then I'm still very interested.

Code: [Select]
public class ComPortEnumerator
{
    [DllImport("Setupapi", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetupDiOpenDevRegKey(IntPtr hDeviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, uint scope,
        uint hwProfile, uint parameterRegistryValueKind, uint samDesired);

    [DllImport("setupapi.dll")]
    private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, Int32 MemberIndex, ref  SP_DEVINFO_DATA DeviceInterfaceData);

    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, UInt32 DeviceInstanceIdSize, out UInt32 RequiredSize);

    [DllImport("setupapi.dll", SetLastError = true)]
    private static extern IntPtr SetupDiGetClassDevs(ref Guid gClass, UInt32 iEnumerator, IntPtr hParent, UInt32 nFlags);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegQueryValueExW", SetLastError = true)]
    private static extern int RegQueryValueEx(IntPtr hKey, string lpValueName, int lpReserved, out uint lpType,
        StringBuilder lpData, ref uint lpcbData);

    [DllImport("advapi32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    private static extern int RegCloseKey(IntPtr hKey);

    private const UInt32 DIGCF_PRESENT    = 0x00000002;
    private const UInt32 DICS_FLAG_GLOBAL = 0x00000001;
    private const UInt32 DIREG_DEV        = 0x00000001;
    private const UInt32 KEY_QUERY_VALUE  = 0x0001;
    private const string GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR = "4D36E978-E325-11CE-BFC1-08002BE10318";

    [StructLayout(LayoutKind.Sequential)]
    private struct SP_DEVINFO_DATA
    {
        public Int32 cbSize;
        public Guid ClassGuid;
        public Int32 DevInst;
        public UIntPtr Reserved;
    };

    public static void logAllUsbToSerialCablesSerialNo()
    {
        Guid guidComPorts = new Guid(GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR);
        Int32 iMemberIndex = 0;
        while (true)
        {
            SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA();
            deviceInfoData.cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
            IntPtr hDeviceInfoSet = SetupDiGetClassDevs(ref guidComPorts, 0, IntPtr.Zero, DIGCF_PRESENT);
            if (hDeviceInfoSet == IntPtr.Zero)
            {
                // Failed to get device information set for the COM ports
                break;
            }
            bool success = SetupDiEnumDeviceInfo(hDeviceInfoSet, iMemberIndex, ref deviceInfoData);
            if (!success)
            {
                // No more devices in the device information set
                break;
            }
            string comPortName = GetDeviceName(hDeviceInfoSet, deviceInfoData);
            string deviceInstancePath = ComPortEnumerator.getDeviceInstancePath(hDeviceInfoSet, deviceInfoData);
            string serialNo = ComPortEnumerator.extractSerialNoFromDeviceInstancePath(deviceInstancePath);
            Debug.WriteLine(comPortName + " has serial number " + serialNo);
            iMemberIndex++;
        }
    }


    private static string getDeviceInstancePath(IntPtr hDeviceInfoSet, SP_DEVINFO_DATA deviceInfoData)
    {
        StringBuilder descriptionBuf = new StringBuilder(256);
        uint length = (uint)descriptionBuf.Capacity;
        bool success = SetupDiGetDeviceInstanceId(hDeviceInfoSet, ref deviceInfoData,
          descriptionBuf, length, out length);
        if (!success)
        {
            throw new Exception("Can not read the Device Instance Path for device " + deviceInfoData.ClassGuid);
        }
        return descriptionBuf.ToString();
    }

    public static string extractSerialNoFromDeviceInstancePath(string deviceInstancePath)
    {
        if ((deviceInstancePath.Contains("VID_0403")) && (deviceInstancePath.Contains("PID_6001")))
        { // 3 MBaud USB-to-serial cable
          // Example: FTDIBUS\VID_0403+PID_6001+FT9FPQAKA\0000
          // The serial number is               FT9FPQAK
            return deviceInstancePath.Substring(26, 8);
        }
        else if ((deviceInstancePath.Contains("VID_0403")) && (deviceInstancePath.Contains("PID_6014")))
        { // 12 MBaud USB-to-serial cable
          // Example: FTDIBUS\VID_0403+PID_6014+FT478YLWA\0000
          // The serial number is               FT478YLW
            return deviceInstancePath.Substring(26, 8);
        }
        return "";
    }


    private static string GetDeviceName(IntPtr pDevInfoSet, SP_DEVINFO_DATA deviceInfoData)
    {
        IntPtr hDeviceRegistryKey = SetupDiOpenDevRegKey(pDevInfoSet, ref deviceInfoData,
            DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
        if (hDeviceRegistryKey == IntPtr.Zero)
        {
            throw new Exception("Failed to open a registry key for device-specific configuration information");
        }

        StringBuilder deviceNameBuf = new StringBuilder(256);
        try
        {
            uint lpRegKeyType;
            uint length = (uint)deviceNameBuf.Capacity;
            int result = RegQueryValueEx(hDeviceRegistryKey, "PortName", 0, out lpRegKeyType, deviceNameBuf, ref length);
            if (result != 0)
            {
                throw new Exception("Can not read registry value PortName for device " + deviceInfoData.ClassGuid);
            }
        }
        finally
        {
            RegCloseKey(hDeviceRegistryKey);
        }

        string deviceName = deviceNameBuf.ToString();
        return deviceName;
    }
}
Logged

FTDI Community

  • Administrator
  • Hero Member
  • *****
  • Posts: 892
    • View Profile

Hello,

The name "USB Serial Port" in Device Manager comes from the default driver INF files. See 2.12.28 driver files.

If you wanted to change the name in Device Manager that would be a complex process:

-Obtaining a PID to use with our VID
-Editing the default driver which breaks signing
-Resigning the driver which incurs costs
-Managing distribution of your driver

So this might not be the best option for you.

What you could do is use FT_PROG to change the Product Description of Serial Number.
Then you could create a simple D2XX program to open by serial number or description.
See the D2XX Programmer's Guide for more information.
Note that this Product Description name change won’t be shown in Device Manager but allows you to program a unique description which can be read through the D2XX APIs.
For example:

ftStatus = FT_OpenEx("Mike's 3 MBaud cable",FT_OPEN_BY_DESCRIPTION,&ftHandle);

Note that there is a limit of the number of characters used in the serial number, description, etc.
See section 4 EEPROM Programming Interface Functions in the D2XX Programmer's Guide.

TN_153 Instructions on Including the D2XX Driver in a Visual Studio Express 2013 Project should help you to get started with D2XX API usage.

You can also check out our C# examples here:

https://ftdichip.com/software-examples/code-examples/csharp-examples/

Best Regards,
FTDI Community
Logged

earnie

  • Newbie
  • *
  • Posts: 7
    • View Profile

What you could do is use FT_PROG to change the Product Description of Serial Number.
Then you could create a simple D2XX program to open by serial number or description.
See the D2XX Programmer's Guide for more information.
Note that this Product Description name change won’t be shown in Device Manager but allows you to program a unique description which can be read through the D2XX APIs.
For example:

ftStatus = FT_OpenEx("Mike's 3 MBaud cable",FT_OPEN_BY_DESCRIPTION,&ftHandle);

Note that there is a limit of the number of characters used in the serial number, description, etc.
See section 4 EEPROM Programming Interface Functions in the D2XX Programmer's Guide.
With this approach I can't use the VCP driver during normal cable use (not during writing to the EEPROM), correct?
Logged

FTDI Community

  • Administrator
  • Hero Member
  • *****
  • Posts: 892
    • View Profile

Hello,

The workaround that I provided relies on the D2XX driver for listing and opening the device with a custom parameter.

Using the VCP driver is limited to open by COM Port number.

The D2XX driver is used to program the device parameters like serial number and description. This is a one time task as it will be stored in the device EEPROM. It can be programmed using FT_PROG or see AN_428 D2XX EEPROM Programming Examples.

You can write some application code that uses both the D2XX and VCP driver functionality.

Best Regards,
FTDI Community
Logged

earnie

  • Newbie
  • *
  • Posts: 7
    • View Profile

The D2XX driver is used to program the device parameters like serial number and description.
Using the VCP-driver, I can view the serial number in the Device Manager by right-clicking -> Properties -> Details tab -> choosing Device instance path in the Property combobox. Can I do something similar to view the contents of the description you're referring to, in the Device Manager when using VCP-driver? Everything I'm able to view in the Device Manager, my C# application can view as well. So my idea is that I program the EEPROM using the D2XX-driver, but once the EEPROM is programmed, from then on I only use the VCP-driver.
Logged

FTDI Community

  • Administrator
  • Hero Member
  • *****
  • Posts: 892
    • View Profile

Hello,

OK Windows uses the VID, PID and Serial Number to enumerate the device, so that's why you can see that in the Device instance path property.
However note that sometimes Windows may not assign the serial number in the registry entry. It may use different nomenclature so it shouldn't be relied upon.

The Product Description can only be read using the D2XX Driver.

Your program could easily use the D2XX driver to find out the information and pass that information to continue using the VCP Driver.
You could use the following D2XX functions to find out the information that you require:

FT_CreateDeviceInfoList
FT_GetDeviceInfoList
FT_Open / FT_OpenEx
FT_GetComPortNumber
FT_Close

Best Regards,
FTDI Community
Logged