PyUSB:从USB设备读取

时间:2019-11-29 12:49:45

标签: usb libusb pyusb

我正在尝试通过USB和pyusb与测量设备进行通信。 在pyusb的文档之后,我有下面的代码。 由于print(dev)返回的内容,甚至似乎是实际设备的响应,因此我想我可能走在正确的轨道上。

现在,我想通过dev.write()发送命令并通过dev.read()接收响应,并且应该获取消息,P1337,1842237,V2.4.0->

但是,我收到一条错误消息:

usb.core.USBError: [Errno None] b'libusb0-dll:err [_usb_reap_async] timeout error\n'

similar post没有帮助。我尝试按照那里的解决方案进行操作,但是不确定现在是否使用软件Zadig弄乱了东西。

我也尝试过类似ep.read(23) ...的尝试,但没有成功。

为什么文档不能提供复制粘贴和运行示例?我在这里做什么错了?

print(dev)的结果

DEVICE ID 5345:1234 on Bus 000 Address 001 =================
 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

代码:

import usb.core
import usb.util

# find our device
# dev = usb.core.find(idVendor=0x5345, idProduct=0x1234)    # although this seem to be the IDs of the device, it will not be found
dev = usb.core.find()

# was it found?
if dev is None:
    raise ValueError('Device not found')

# set the active configuration. With no arguments, the first
# configuration will be the active one
dev.set_configuration()

# get an endpoint instance
cfg = dev.get_active_configuration()
intf = cfg[(0,0)]

ep = usb.util.find_descriptor(
    intf,
    # match the first OUT endpoint
    custom_match = \
    lambda e: \
        usb.util.endpoint_direction(e.bEndpointAddress) == \
        usb.util.ENDPOINT_OUT)

assert ep is not None

print(dev)

msg = '*IDN?'
ep.write(msg)

# write the data
# assert len(dev.write(3, msg, 100)) == len(msg)    # original line from documentation, however, TypeError: object of type 'int' has no len()
assert dev.write(3, msg, 100) == len(msg)           # without len(dev.write(....)) seems to work
ret = dev.read(0x81, 23, 1000)
sret = ''.join([chr(x) for x in ret])
assert sret == msg
print(sret)

编辑:

在Turbo J的注释之后,仅向命令中添加\n并不能解决超时错误的问题。

5 个答案:

答案 0 :(得分:2)

我想除非有人已经遇到了同样的问题,否则 没有机会 。 我为你们所有人(@Alex P。,@ Turbo J,@ igrinis,@ 2xB)花了很多时间提出建议提供帮助而感到抱歉。

我的发现:(我希望它们对其他人有用)

  1. PyUSB似乎一切正常。
  2. 供应商提供了过时且错误的文档。我非常希望他们能尽快更新其首页上的文档。
  3. 发送命令:SDSLSCPI#无需进入SCPI模式(但实际上会导致崩溃/重新启动)
  4. 例如::CHAN1:SCAL 10v是错误的,它必须为:CH1:SCALe 10v(显然,命令 不能缩写,尽管在文档中提到:CH1:SCAL 10v应该也可以。)
  5. 手册中缺少获取数据:DATA:WAVE:SCREen:CH1?的基本命令。

它为我工作的方式(到目前为止):

以下是我期望供应商/制造商提供的最少代码。但是相反,我浪费了很多时间调试他们的文档。 但是,仍然发生了一些奇怪的事情,例如看来,仅当您事先请求标头时,您才获得数据。但是,这不是原始问题的主题。

代码:

### read data from a Peaktech 1337 Oscilloscope (OWON)
import usb.core
import usb.util

dev = usb.core.find(idVendor=0x5345, idProduct=0x1234)

if dev is None:
    raise ValueError('Device not found')
else:
    print(dev)
    dev.set_configuration()

def send(cmd):
    # address taken from results of print(dev):   ENDPOINT 0x3: Bulk OUT
    dev.write(3,cmd)
    # address taken from results of print(dev):   ENDPOINT 0x81: Bulk IN
    result = (dev.read(0x81,100000,1000))
    return result

def get_id():
    return send('*IDN?').tobytes().decode('utf-8')

def get_data(ch):
    # first 4 bytes indicate the number of data bytes following
    rawdata = send(':DATA:WAVE:SCREen:CH{}?'.format(ch))
    data = []
    for idx in range(4,len(rawdata),2):
        # take 2 bytes and convert them to signed integer using "little-endian"
        point = int().from_bytes([rawdata[idx], rawdata[idx+1]],'little',signed=True)
        data.append(point/4096)  # data as 12 bit
    return data

def get_header():
    # first 4 bytes indicate the number of data bytes following
    header = send(':DATA:WAVE:SCREen:HEAD?')
    header = header[4:].tobytes().decode('utf-8')
    return header

def save_data(ffname,data):
    f = open(ffname,'w')
    f.write('\n'.join(map(str, data)))
    f.close()

print(get_id())
header = get_header()
data = get_data(1)
save_data('Osci.dat',data)
### end of code

结果 :(使用gnuplot)

enter image description here

答案 1 :(得分:1)

一旦在*IDN?查询中从设备获得响应,就应该可以了。这是SCPI;)

尝试发送:CHAN1:SCAL 10v,然后观看显示。应该将通道1的垂直刻度更改为10V / div。

观看此video,它将帮助您抓紧时间。

关于您有关read()参数的问题。引用PyUSB源:

def read(self, endpoint, size_or_buffer, timeout = None):
    r"""Read data from the endpoint.
    This method is used to receive data from the device. The endpoint
    parameter corresponds to the bEndpointAddress member whose endpoint
    you want to communicate with. The size_or_buffer parameter either
    tells how many bytes you want to read or supplies the buffer to
    receive the data (it *must* be an object of the type array).
    The timeout is specified in miliseconds.
    If the size_or_buffer parameter is the number of bytes to read, the
    method returns an array object with the data read. If the
    size_or_buffer parameter is an array object, it returns the number
    of bytes actually read.
    """

省略超时时,将Device.default_timeout property用作操作超时。值以毫秒为单位。

如果将缓冲区大小设置得足够大,则只会得到实际读取的字节。所以您的期望是正确的。

答案 2 :(得分:1)

首先请注意,@ igrinis发布了一个视频,显示您想要到达的地方。

(如@igrinis所述:)对于read(...)中的第二个值,您在理论上是正确的。好消息是实际上您经常可以请求更长的答案。所以尝试例如请求256个字节,然后看是否能解决您当前的代码。

如果这不能解决您的问题:

您可以尝试使用第二个PC /笔记本电脑(例如来自)的软件有能力与设备进行通信的制造商,并使用Wireshark(已安装USBPcap)读取设备通信。发送和接收的USB批量数据写入Wiresharks的“剩余捕获数据”字段中。通过查看,您可以比较脚本发送的内容以及发现错误的外观。您可以通过右键单击并选择“应用为列”,将其作为一列添加到数据包列表中。您的问题可能例如是您的命令对大端或小端的编码。

PyUSB的文档:

[更新]在一个很棒的评论中添加了提示,这些提示已经给出了一些答案以及更多信息。

答案 3 :(得分:0)

  

msg = '*IDN?'

这不是完整的SCPI命令:结尾缺少换行符\n

这也是为什么设备无法通过USB发送答案的原因。

答案 4 :(得分:0)

默认情况下,OWON设备上未启用SCPI。根据{{​​3}}的第3页,您需要发送:SDSLSCPI#命令以切换到SCPI模式。