This is an updated and shortened question.
Communicating with a USB-device should be easy via PyUSB. So, I'm trying to read from a USB-device (oscilloscope) using PyUSB under Win10. Apparently, the USB-driver (libusb-win32 v1.2.6.0) is installed correctly since the device is found and I get some response from print(dev)
(see below).
From this I can see that the output endpoint address is 0x3
and the input endpoint address is 0x81
According to the Oscilloscope manual, I'm supposed to send :SDSLSCPI#
to the device to set it into SCPI-mode and should get the reponse ':SCPION'.
However, when sending :SDSLSCPI#
the monitor of the oscilloscope reproduceably will freeze and it will restart.
If I send *IDN?
I should get the response ,P1337,1842237,V2.4.0->
. But only if the device is already in SCPI-mode. Apparently, it is not and I get a timeout error (see below).
So, what am I doing wrong here? What information am I missing in the PyUSB tutorial. Am I using the wrong PyUSB commands/parameters or is it about missing additional drivers or is it about the hardware, either Win10 or the device hardware? Thank you for hints on how to find out what's going wrong.
By the way, what is the second value in dev.read(0x81,7)
? Number of bytes to read? Well, usually I don't know how many bytes the device will send. I was expecting a command to read until a linefeed or some other terminator character within the timeout time. Where can I find "fool-proof" documentation, tutorials and examples about PyUSB?
Code:
import usb.core
import usb.util
dev = usb.core.find(idVendor=0x5345, idProduct=0x1234)
if dev is None:
raise ValueError('Device is not found')
# device is found :-)
print(dev)
dev.set_configuration()
msg = ':SDSLSCPI#'
print("Write:", msg, dev.write(3,msg))
print("Read:", dev.read(0x81,7))
Output from 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
Error message:
Traceback (most recent call last):
File "Osci.py", line 15, in <module>
print("Read:", dev.read(0x81,7))
File "C:\Users\Test\Programs\Python3.7.4\lib\site-packages\usb\core.py", line 988, in read
self.__get_timeout(timeout))
File "C:\Users\Test\Programs\Python3.7.4\lib\site-packages\usb\backend\libusb0.py", line 542, in bulk_read
timeout)
File "C:\Users\Test\Programs\Python3.7.4\lib\site-packages\usb\backend\libusb0.py", line 627, in __read
timeout
File "C:\Users\Test\Programs\Python3.7.4\lib\site-packages\usb\backend\libusb0.py", line 431, in _check
raise USBError(errmsg, ret)
usb.core.USBError: [Errno None] b'libusb0-dll:err [_usb_reap_async] timeout error\n'
Update:
I got a reply from the vendor. And he confirms that the oscilloscope (or at least this specific series) crashes when sending the command :SDSLSCPI#
. He will contact the developers which will back next week. OK, it seems so far no chance for me to get it to run with this specific device and the available documentation :-(.
PyUSB offers easy USB devices communication in Python. It should work without additional code in any environment with Python >= 3.6, ctypes and a pre-built USB backend library (currently: libusb 1. x, libusb 0.1. x or OpenUSB).
I guess there was no chance to answer this question unless somebody already went through the very same problems. I'm sorry for all of you (@Alex P., @Turbo J, @igrinis, @2xB) who took your time to make suggestions to help.
My findings: (I hope they will be useful to others):
:SDSLSCPI#
is not necessary to enter SCPI-mode (but actually leads to a crash/restart):CHAN1:SCAL 10v
is wrong, it has to be :CH1:SCALe 10v
(commands apparenty can't be abbreviated, although mentioned in the documentation that :CH1:SCAL 10v
should also work.):DATA:WAVE:SCREen:CH1?
was missing in the manual.The way it is working for me (so far):
The following would have been the minimal code I expected from the vendor/manufacturer. But instead I wasted a lot of time debugging their documentation. However, still some strange things are going on, e.g. it seems you get data only if you ask for the header beforehand. But, well, this is not the topic of the original question.
Code:
### 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
Result: (using gnuplot)
First note that @igrinis posted a video showing what you want to reach.
(As stated by @igrinis:) For the second value in read(...)
, you are right in theory. Good thing is that practically often enough you can request way longer answers. So try e.g. requesting 256 bytes and look if that fixes your current code.
If that does not solve your issue:
You can try to have a second PC/Laptop around with software from e.g. the manufacturer that is capable of communicating with the device, and use Wireshark (with USBPcap installed) to read the device communication. The USB bulk data transmitted and received is written in Wiresharks "Leftover Capture Data" field. By looking at that you can compare what your script sends and how it should look like to spot mistakes. You can add it as a column to the list of packets by right-clicking and selecting "Apply as column". Your problem might e.g. be the encoding of your command to big or little endian.
Documentation for PyUSB:
Mainly Python Docstring, so call help(...)
on every object/function
you get from PyUSB. Alternatively, search the PyUSB source code for every function and object you want to read the Docstring of.
https://github.com/pyusb/pyusb/blob/master/README.rst
https://github.com/pyusb/pyusb/blob/master/docs/faq.rst
https://github.com/pyusb/pyusb/blob/master/docs/tutorial.rst
https://github.com/pyusb/pyusb/wiki
[Update] Added hints to a great comment that already gave some of the answers and more.
msg = '*IDN?'
This is not a complete SCPI command: It is missing the newline \n
character at the end.
That is also why the device was not able to send an answer via USB.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With