Communication via PyUSB to Agilent E4980A

122 views Asked by At

Communication to USB devices drives me crazy. It's now at least the third USB device creating problems:

pyusb and libusb are installed and apparently found. My configuration: Windows 10, Python 3.11.3, libusb-1.0 (v1.0.26.11724), pyUSB (v1.2.1)

Script:

import usb.core
import usb.util
from usb.backend import libusb1

backend = libusb1.get_backend(find_library=lambda x: r'C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\libusb\_platform\_windows\x64\libusb-1.0.dll')

dev = usb.core.find(idVendor=2391, idProduct=2313, backend=backend)   # Agilent E4980A
print(dev)

Output: (well, device is apparently found)

DEVICE ID 0957:0909 on Bus 001 Address 012 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0x0957
 idProduct              : 0x0909
 bcdDevice              :  0x100 Device 1.0
 iManufacturer          :    0x1 Error Accessing String
 iProduct               :    0x2 Error Accessing String
 iSerialNumber          :    0x3 Error Accessing String
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 0 mA ====================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x27 (39 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0
   bmAttributes         :   0xc0 Self Powered
   bMaxPower            :    0x0 (0 mA)
    INTERFACE 0: Application Specific ======================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x3
     bInterfaceClass    :   0xfe Application Specific
     bInterfaceSubClass :    0x3
     bInterfaceProtocol :    0x1
     iInterface         :    0x4 Error Accessing String
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x86: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x86 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x88: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x88 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :    0x2 (2 bytes)
       bInterval        :    0x1

However, as soon as I try to send a command (it doesn't matter if with or w/o newline characters \r or \n),

cmd = '*IDN?'+'\r'
dev.write(2,cmd)

I will get an error:

Traceback (most recent call last):
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 236, in get_interface_and_endpoint
    return self._ep_info[endpoint_address]
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
KeyError: 2

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\Lab\Scripts\tbUSB.py", line 12, in <module>
    dev.write(2,'*IDN?'+'\r')
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 986, in write
    intf, ep = self._ctx.setup_request(self, endpoint)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 113, in wrapper
    return f(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 228, in setup_request
    intf, ep = self.get_interface_and_endpoint(device, endpoint_address)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 113, in wrapper
    return f(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 238, in get_interface_and_endpoint
    for intf in self.get_active_configuration(device):
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 113, in wrapper
    return f(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 249, in get_active_configuration
    self.managed_open()
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 113, in wrapper
    return f(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 131, in managed_open
    self.handle = self.backend.open_device(self.dev)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\backend\libusb1.py", line 804, in open_device
    return _DeviceHandle(dev)
           ^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\backend\libusb1.py", line 652, in __init__
    _check(_lib.libusb_open(self.devid, byref(self.handle)))
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\backend\libusb1.py", line 600, in _check
    raise NotImplementedError(_strerror(ret))
NotImplementedError: Operation not supported or unimplemented on this platform

If I do first a set configuration() as recommended in the PyUSB tutorial, I get the following error:

Traceback (most recent call last):
  File "C:\Users\Lab\Scripts\tbUSB.py", line 10, in <module>
    dev.set_configuration()
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 915, in set_configuration
    self._ctx.managed_set_configuration(self, configuration)
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 113, in wrapper
    return f(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 158, in managed_set_configuration
    self.managed_open()
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 113, in wrapper
    return f(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 131, in managed_open
    self.handle = self.backend.open_device(self.dev)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\backend\libusb1.py", line 804, in open_device
    return _DeviceHandle(dev)
           ^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\backend\libusb1.py", line 652, in __init__
    _check(_lib.libusb_open(self.devid, byref(self.handle)))
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\backend\libusb1.py", line 600, in _check
    raise NotImplementedError(_strerror(ret))
NotImplementedError: Operation not supported or unimplemented on this platform

Similar posts on StackOverflow with the same error message were not helpful:

There is a I/O suite from Keysight/Agilent which seems to work, however, I wanted to avoid the installation of about 266 MB or 1.33 GB on every PC if I could do it with a simple driver of about a few hundred Kilobytes.

I'm also aware that there are other Python libraries (e.g. pyMeasure, even with some code for Agilent E4980A), which I also unsuccessfully tried because of (at least for me) insufficient documentation without minimal working examples. But, I don't want to use these libraries, but simply send some commands and receive some data via PyUSB.

Apparently, the PC can get some information of the device via Python, but I can't write and read anything. Am I missing another driver or something?

Update: (progress?)

In an old post from 2012 in the PyUSB mailing list I read that PyUSB requires special drivers for the device which can be installed via Zadig. The original driver of the Keysight(Agilent) IO Library Suite Usbtmc (v16.3.17614.0) was replaced by libusbK (v3.1.0.0).

enter image description here

Now, the Windows Device Manager indicates that the Oscilloscope P1337 and the AgilentE4980A use libusbK as driver.

enter image description here

As a negative side effect, the KeysightIOLibSuite doesn't work anymore and even not after a re-installation and I don't know to get it back. But well, for me it is more important that it will run with PyUSB.

If I run the following script, the P1337 works fine, but the E4980A always stops with an timeout error.

Script:

import usb.core
import usb.util
from usb.backend import libusb1

backend = libusb1.get_backend(find_library=lambda x: r'C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\site-packages\libusb\_platform\_windows\x64\libusb-1.0.dll')

devs = usb.core.find(find_all=True, backend=backend)

id_e4980a = (0x0957,0x0909)   # Agilent E4980A
id_p1337  = (0x5345,0x1234)   # OWON/Peaktech P1337

def get_device(devs, id_dev):
    dev = None
    for x in devs:
        # print("idVendor: 0x{:04x}, idProduct: 0x{:04x}, Manufacturer: {}".format(x.idVendor, x.idProduct, x.iManufacturer))
        if id_dev == (x.idVendor, x.idProduct): dev = x
    return dev

def check_dev(id_dev,ep1,ep2):
    dev = get_device(devs, id_dev)
    print(dev)
    dev.set_configuration()
    cfg  = dev.get_active_configuration()
    intf = cfg[(0,0)]
    ep   = usb.util.find_descriptor(intf,custom_match = lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT)
    print(ep)
    print(dev.write(ep1,'*IDN?'))
    print(dev.read(ep2, 100).tobytes().decode('utf-8'))

check_dev(id_p1337,0x3,0x81)
check_dev(id_e4980a,0x2,0x86)  

Result: (for P1337, all ok)

DEVICE ID 5345:1234 on Bus 002 Address 003 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0x5345
 idProduct              : 0x1234
 bcdDevice              :  0x294 Device 2.94
 iManufacturer          :    0x1 System CPU
 iProduct               :    0x2 Oscilloscope
 iSerialNumber          :    0x3 SERIAL
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 500 mA ==================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x20 (32 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x5 Bulk Data Configuration
   bmAttributes         :   0xc0 Self Powered
   bMaxPower            :   0xfa (500 mA)
    INTERFACE 0: Physical ==================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x2
     bInterfaceClass    :    0x5 Physical
     bInterfaceSubClass :    0x6
     bInterfaceProtocol :   0x50
     iInterface         :    0x4 Bulk Data Interface
      ENDPOINT 0x81: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x3: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x3 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x3: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x3 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
5
,P1337,1842237,V2.4.0->

Result: (for E4980A, timeout error)

DEVICE ID 0957:0909 on Bus 002 Address 007 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0x0957
 idProduct              : 0x0909
 bcdDevice              :  0x100 Device 1.0
 iManufacturer          :    0x1 Agilent Technologies
 iProduct               :    0x2 E4980A
 iSerialNumber          :    0x3 MY46203491
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 0 mA ====================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x27 (39 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0
   bmAttributes         :   0xc0 Self Powered
   bMaxPower            :    0x0 (0 mA)
    INTERFACE 0: Application Specific ======================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x3
     bInterfaceClass    :   0xfe Application Specific
     bInterfaceSubClass :    0x3
     bInterfaceProtocol :    0x1
     iInterface         :    0x4 tmc48挸
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x86: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x86 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0
      ENDPOINT 0x88: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x88 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :    0x2 (2 bytes)
       bInterval        :    0x1
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :  0x200 (512 bytes)
       bInterval        :    0x0

Error:

Traceback (most recent call last):
  File "C:\Users\Lab\Scripts\tbUSB.py", line 31, in <module>
    check_dev(id_e4980a,0x2,0x86)
  File "C:\Users\Lab\Scripts\tbUSB.py", line 27, in check_dev
    print(dev.write(ep1,'*IDN?'))
          ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\core.py", line 989, in write
    return fn(
           ^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\backend\libusb1.py", line 837, in bulk_write
    return self.__write(self.lib.libusb_bulk_transfer,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\backend\libusb1.py", line 938, in __write
    _check(retval)
  File "C:\Users\Lab\AppData\Local\Programs\Python\Python311\Lib\site-packages\usb\backend\libusb1.py", line 602, in _check
    raise USBTimeoutError(_strerror(ret), ret, _libusb_errno[ret])
usb.core.USBTimeoutError: [Errno 10060] Operation timed out

So, at least with the libusbK driver the dev.set_configuration() doesn't lead to an error, but now there is a timeout error.

Why this? How to solve this? What else is missing?

1

There are 1 answers

0
Chris St John On

It's important to understand a few things here...

PyUSB uses libusb which is a way to write user-mode USB device drivers. You correctly identified that there still has to be a kernel-mode driver (e.g. installed by Zadig) but I think maybe you were choosing the wrong one. For my projects I'm using libusb-win32 rather than libusbK.

The we come to another point which is that Zadig replaces the original kernel-mode driver with libusb, which will stop the original software from working. There's not much you can easily do about that: the user-mode and kernel-mode parts need to match.

Finally, way back at the top of your very detailed explaination you appeared to be trying to send an ASCII string to a USB BULK OUT endpoint. Since the Interface descriptor says "Application Specific" there are no guarantees about what format commands might have on this endpoint. It might be serial (with CR, LF etc) or it might not. I'd suggest installing a software sniffer and having a look at what the original software is doing, if you've not done so already.