API Access on Linux

Our current USBC-TKEY-based setup for measurement of power consumption uses a Ubuntu 20.04 machine for tracking the parameters on the VBUS line. Since the USBC-TKEY has a command line interface accessed over the serial port, we had developed a Python script to record a CSV file with the required data. In order to experiment with the APIs provided by ChargerLAB for the KM003C, a SanDisk Professional PRO G40 Thunderbolt 3 portable SSD was connected to a Thunderbolt 3 port of a Quartz Canyon NUC with the KM003C acting as a man-in-the-middle (additional details in a later section discussing the KM003C in action). The HID port of the KM003C was connected to the Ubuntu machine using a Type-C to Type-A cable.

The API document makes a note of the KM003C presenting itself with three bidirectional interfaces. As only Windows support is claimed, it is no surprise that only the HID endpoints get exposed via the usbhid driver. The WinUSB endpoints belong to a vendor specific class, and the virtual serial port appears as a CDC data interface.

In exploring the KM003C as an alternative to the USBC-TKEY, we wanted to retain the Python script infrastructure (because it tied in well with the other components of our direct-attached storage evaluation infrastructure). It turned out that Python's libusb module could act as a WinUSB alternative with its ability to access the endpoints under the Vendor Specific Class. This module requires the end user to be aware of the raw data that needs to be sent across the interface, and the format in which data is returned on the other endpoint.

The relevant modules to import in Python3 for libusb support are usb.core, usb.util, and usb.control. Since the vendor and product ID of the KM003C are known from the output of the usbdevices command, they are used directly to open the device for access through the script. Reliable scripting needs to ensure that the device is successfully found. For demonstration purposes, the variable contents are displayed, as shown above.

While the libusb module aims to be cross-platform, we did find that the steps below to detach the kernel driver did not work on Windows. libusb needs exclusive access to all the interfaces over which communication is expected to take place. For demonstration purposes, the code extract above cycles through a hard-coded number of interfaces (4), but it is possible to obtain that number from other functions in the module. The extract checks if an interface has a kernel driver attached to it, and detaches it in that case. It then proceeds to claim the interface for access through the script's session.

In order to retrieve the power consumption data from the device, a command needs to be transferred via the EP 1 OUT endpoint (with endpoint address 0x01). As ChargerLAB mentions, we can also use the EP 5 OUT and EP 3 OUT ones at addresses 0x5 and 0x3, but the response needs to be monitored at the corresponding EP IN endpoint. The command itself is in the form of a 32-bit word, and its encoding is detailed in the API document. In the script extract below, the 32-bit word is 0x0C000200. It corresponds to a GET_DATA command for the ADC data with a command ID of 0x0. I found during the course of experimentation that the ID in [23:16] could be varied from 0x0 to 0xFF without any loss in functionality. Since the command remains the same throughout the reading loop (we attempt to read out the power data 20 times with a gap of 100ms between each read), the command word is initialized outside the loop.

The first step in the loop is to obtain a timestamp for the command written out to endpoint address 0x1. This write is followed by the read to endpoint address 0x81 (the EP IN corresponding to the EP OUT used in the write). The read is blocking, i.e, it waits for the data to be sent back by the device and there is a default timeout associated with this command. The received data is then parsed out into appropriate components (instantaneous VBUS voltage and current, and the averaged VBUS voltage and current) based on the struct specified in the API document.

The HID demonstration code (supplied as a Visual Studio 2019 project) tagged along with the API document read out the averaged voltage and current. The documentation did not specify the averaging duration or the sampling rate. In the initial trials, the code did not read out the instantaneous values, and I was left frustrated as to why the averaged value was changing only once every second instead of getting updated for every read out. I spent quite a bit of time trying out the same code with different endpoints, and even different OSes. I even went to the extent of using Wireshark along with usbpcap in an attempt to trace the commands sent and data received by ChargeLAB's closed-source Windows software. It was only after careful analysis of the packet capture did I realize that the average VBUS voltage and currents were changing only once ever second even in the traffic triggered by the software. The instantaneous data of interest actually turned out to be the first two words in the returned ADC data struct. The time taken for the read process and display of the CSV data is computed and the code segment is left idle before starting the next loop iteration 100ms later. Each line in the output data stream shows the timestamp, instantaneous voltage, instantaneous current, and instantaneous computed power, followed by the averaged versions of all three. Given the details of the averaging process, it is possible that the computed averaged power consumption can also be interpreted as the energy consumed by the sink (device, in this case) since the last change in that parameter.

As a final step in the script, a cleanup is performed so that all open accesses to the device via libusb are flushed out, and the device is made accessible to other programs. The script can then be terminated safely.

Hardware Design and Software Interface API Access on Windows
Comments Locked

20 Comments

View All Comments

  • ballsystemlord - Wednesday, July 5, 2023 - link

    It's unfortunate that the KM003C is windowz only closed source.
  • ganeshts - Wednesday, July 5, 2023 - link

    It is not a major problem, as I have demonstrated in a couple of sections. You can use libusb or any other serial port access program to communicate with the KM003C and create a wrapper suited for your particular use-case.

    That said, one aspect I should have mentioned in the conclusions is related to the ability to update the firmware independent of the closed-source Windows program. Coupled with better API documentation, that would basically make the device pretty functional without having to rely on the closed-source program.
  • ballsystemlord - Wednesday, July 5, 2023 - link

    Oh, I see.
  • QChronoD - Wednesday, July 12, 2023 - link

    Since you posted a screenshot of the code for connecting and pulling out the relevant power data, are you OK with people using it? Are you planning on posting it on github or somewhere?
  • ganeshts - Thursday, July 13, 2023 - link

    Absolutely! Code is free for anyone to start off with as the base point for their own programs.

    My github account has been idle for a few years now. I will probably upload a refined version of the code that appears in this review later this year when I get time. The only reason I didn't post the code as text is to avoid making the review appear like a StackOverflow post :)
  • TrevorH - Thursday, July 6, 2023 - link

    You could have saved me from going straight to the last page by putting the price at the top of the article. At $10 I would have snapped your hand off, at $110 it's a definite no.
  • ganeshts - Thursday, July 6, 2023 - link

    I think it would be impossible to hit $10 for this type of product. Even BOM cost for microcontroller + 5 ADCs would easily exceed that (even if they are bought in 1Ku quantities). Need to add software development and distribution costs. I would imagine break-even is itself around $40 - $50.
  • ads295 - Saturday, July 8, 2023 - link

    +1
    It's not for "hobbyists" as mentioned in the article.
  • Bp_968 - Thursday, August 3, 2023 - link

    Not sure I agree that 50$ puts it out of reach of "hobbyists". I'm a hobbyist and own a oscilloscope, and a number of other test tools more expensive then 50-100$. If all your doing is seeing what voltage your usb battery is negotiating with your switch or steamdeck then yes, this device is overkill. But if you building anything that's using PD or other usb-c features this could be extremely useful.

    I'm unlikely to build anything anytime soon that would require the features this thing supports but I still want one! ;)
  • DanNeely - Thursday, July 6, 2023 - link

    "The KM003C can support up to 50V / 6A (full USB-PD 3.1 specifications, with EPR up to 240W)."

    Unless this device is going significantly beyond USB-PD levels I believe this should read 5A not 6.

Log in

Don't have an account? Sign up now