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
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.
Once you get the response from the device on *IDN?
query you should be good to go. This is SCPI ;)
Try to send :CHAN1:SCAL 10v
, and watch the display. It should change the vertical scale of channel 1 to 10V/div.
Watch this video, it will help you get a grip.
On your question about read()
parameters. Quoting the PyUSB source:
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.
"""
When the timeout is omitted, it is used the Device.default_timeout property
as the operation timeout. Values are in milliseconds.
If you set the buffer size big enough, you will get only the bytes actually read. So your expectations are correct.
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.
SCPI is not enabled by default on OWON devices. According to the page#3 of the manual you need to send :SDSLSCPI#
command to switch to the SCPI mode.