Why can't I send data to a USB device?

132 views Asked by At

I'm having trouble sending data to a connected USB device.

I am trying to write a program to automate data collection in some experiments. My measurement equipment is moved around by a Thorlabs PT3/M-Z8 three-axis motorised stage. Each of the axes is controlled separately by a KDC101 "K-cube" DC servo motor controller (three K-cubes in total for our setup). The K-cubes are all plugged into a KCH301 USB hub so that there's just one USB cable to my laptop. I'm working on an M3 MacBook Pro running MacOS Sonoma 14.2.1 but I would like to make my program platform-independent so that anyone in our lab can use it (we have an approx. 50/50 split between Mac and Windows). The KCH301 USB hub and three KDC101 controllers are detected by the "System Information" app, so I know they definitely work!

I am using C# targeting .NET 8. I'm using LibUsbDotNet 2.2.29 (installed from NuGet) to keep everything platform-independent. At some point I also had to install Mono and add LibUsb-1.0.0.dylib and LibUsb-1.0.dylib to the project/bin/debug/net8.0 file (still not 100% certain what they do).

I am aware that Thorlabs have a Kinesis DLL that can be used, but it only works on .NET framework 4.8.1 which doesn't help my platform-independent goal. Instead, Thorlabs recommend I use their APT protocol to send byte commands directly to the KDC101. The electrical interface within each KDC101 uses a FT232BM USB peripheral chip (made by FTDI) to communicate with the host PC. This is a USB2.0 compliant USB1.1 device which "provides a serial port interface to the embedded system (i.e., Thorlabs controller) and USB interface to the host control PC".

From the APT protocol:

FTDI supply device drivers and interfacing libraries (for Windows, Linux, and other platforms) used to access the USB chip. Before any PC USB communication can be established with an Thorlabs controller, the client program is required to set up the necessary FTDI chip serial port settings used to communicate to the Thorlabs controller embedded system.

I have installed FTDI's D2XX 1.4.24 driver for MacOS (which incidentally also seems to use LibUsb!) as well as the D2xxHelper. I have followed the install instructions detailed in their README document and the helpful video install guide. About half-way through the video he says that once the D2XX drivers are installed "you may link to them in your build using -lftd2xx". This is where I get stuck. How do I "link" to a driver? For reference, I'm using the JetBrains Rider IDE.

I tried sending some basic commands to the KDC101 anyway, just to see if it would work. I used the following test code:

using System.IO.Ports;
using LibUsbDotNet;
using LibUsbDotNet.Main;

namespace USB_Communication;

public class UsbPeripheralChipFT232BM
{
    //Put your Product Id Here
    private const int ProductId = 0xfaf0;

    //Put your Vendor Id Here
    private const int VendorId = 0x0403;

    public static void Main()
    {
        FindDevice();
    }

private static void FindDevice()
    {
        // Find your USB device by its vendor ID and product ID
        UsbDeviceFinder finder = new UsbDeviceFinder(0x0403, 0xfaf0);

        // Initialize the USB context
        using (UsbDevice wholeUsbDevice = UsbDevice.OpenUsbDevice(finder))
        {
            if (wholeUsbDevice == null)
            {
                Console.WriteLine("Device not found!");
                return;
            }

            // Open the USB device
            if (!wholeUsbDevice.Open())
            {
                Console.WriteLine("Could not open USB device!");
                return;
            }
            
            //Open up the endpoints
            var writeEndpoint = wholeUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep01);
            var readEnpoint = wholeUsbDevice.OpenEndpointReader(ReadEndpointID.Ep01);
            
            var dataToSend = new byte[64];
            dataToSend[0] = 43;
            dataToSend[1] = 04;
            dataToSend[2] = 01;
            dataToSend[3] = 00;
            dataToSend[4] = 22;
            dataToSend[5] = 01;
            
            writeEndpoint.Write(dataToSend, 5000, out var numBytesWritten);

            Console.WriteLine("Number of bytes written: " + numBytesWritten);
        }
    }
}

The command "43, 04, 01, 00, 22, 01" is an example taken directly from the APT protocol. It should cause the KDC101 to begin a homing sequence. Unfortunately, my KDC101 doesn't do anything and I get the response in the console "Number of bytes written: 0". I've tried changing the endpoint number but that doesn't seem to help.

So, after all that context, my question is: Why can't I send this message through to the USB device?

Update

I have had partial success by updating to LibUsbDotNet 3.0.102 from NuGet:

using LibUsbDotNet;
using LibUsbDotNet.LibUsb;
using LibUsbDotNet.Main;

namespace USB_Communication;

public class UsbPeripheralChipFT232BM
{
    public static void Main()
    {
        using (var context = new UsbContext())
        {
            context.SetDebugLevel(LogLevel.Info);

            //Get a list of all connected devices
            using var usbDeviceCollection = context.List();

            //Narrow down the device by vendor and pid
            var selectedDevice = usbDeviceCollection.FirstOrDefault(d => d.ProductId == 0xfaf0 && d.VendorId == 0x0403);

            //Open the device
            if (selectedDevice != null)
            {
                selectedDevice.Open();

                //Get the first config number of the interface
                selectedDevice.ClaimInterface(selectedDevice.Configs[0].Interfaces[0].Number);

                //Open up the endpoints
                var writeEndpoint = selectedDevice.OpenEndpointWriter(WriteEndpointID.Ep02);
                var readEnpoint = selectedDevice.OpenEndpointReader(ReadEndpointID.Ep03);

                //Create a buffer with some data in it
                var dataToSend = new byte[6];
                dataToSend[0] = 43;
                dataToSend[1] = 04;
                dataToSend[2] = 01;
                dataToSend[3] = 00;
                dataToSend[4] = 22;
                dataToSend[5] = 01;

                //Write six bytes
                writeEndpoint.Write(dataToSend, 3000, out var bytesWritten);
                
                Console.WriteLine("Number of bytes written: " + bytesWritten);
            }
        }
    }
}

This correctly identified the three KDC101 brushless motor controllers and returned the line "Number of bytes written: 6" but the motor did not initiate a homing move so I'm guessing the message still isn't getting through.

0

There are 0 answers