Closed whitequark closed 5 years ago
Here is the proposed fix, implemented in edb6b0020b26f431bee3ba085d1bea8a232a04f6 (and released as 1.6.6):
handle.getASCIIStringDescriptor(handle.getDevice().getSerialNumberDescriptor())
The idea of USBDevice
is that it gives access to what can be accessed without opening the device: what the OS has cached and exposes after it enumerated the device. Opening an USBDevice
means producing an USBHandle
, so to me the fix is rather to call methods available on USBHandle
.
getSerialNumber
along with getManufacturer
and getProduct
are exceptions to this rule, and rather intended as shortcuts to be used while looking for a device.
Sadly, I did not expose the raw descriptor ids for these 3, which is what I just added in 1.6.6 .
I agree that your proposed solution solves the problem, but I feel like it doesn't go far enough. It is tempting to use the API in what appears the easiest possible way, and most people would likely just do that, and here that would still produce an obscure portability hazard.
This is veering into opinion territory though, so feel free to close this if this doesn't mesh with your approach to API design.
Sorry if I don't get the above, but when I append getSerialNumber() to your example
import usb1
with usb1.USBContext() as context:
for device in context.getDeviceIterator(skip_on_error=True):
print(
"ID %04x:%04x" % (device.getVendorID(), device.getProductID()),
"->".join(
str(x)
for x in ["Bus %03i" % (device.getBusNumber(),)]
+ device.getPortNumberList()
),
"Device",
device.getDeviceAddress(),
"SerialNumber",
device.getSerialNumber(),
)
I get the following output libusb1 => 1.6.6
/usr/bin/python3.6 /home/kevin/Git_Python/pySerial/sandbox/scratch_08.py
Traceback (most recent call last):
File "/home/kevin/Git_Python/pySerial/sandbox/scratch_08.py", line 16, in <module>
device.getSerialNumber(),
File "/usr/local/lib/python3.6/dist-packages/usb1/__init__.py", line 2057, in getSerialNumber
self.device_descriptor.iSerialNumber)
File "/usr/local/lib/python3.6/dist-packages/usb1/__init__.py", line 2018, in _getASCIIStringDescriptor
return self.open().getASCIIStringDescriptor(descriptor)
File "/usr/local/lib/python3.6/dist-packages/usb1/__init__.py", line 2092, in open
mayRaiseUSBError(libusb1.libusb_open(self.device_p, byref(handle)))
File "/usr/local/lib/python3.6/dist-packages/usb1/__init__.py", line 133, in mayRaiseUSBError
__raiseUSBError(value)
File "/usr/local/lib/python3.6/dist-packages/usb1/__init__.py", line 125, in raiseUSBError
raise __STATUS_TO_EXCEPTION_DICT.get(value, __USBError)(value)
usb1.USBErrorAccess: LIBUSB_ERROR_ACCESS [-3]
Process finished with exit code 1
sorry to be a pain
Hello again!
code snippet of USB serial number, kind of works
import usb1
# arduino - 0x2341:0x0043
VENDOR_ID = 0x2341
PRODUCT_ID = 0x0043
with usb1.USBContext() as context:
device = context.getByVendorIDAndProductID(
VENDOR_ID, PRODUCT_ID, skip_on_error=True, skip_on_access_error=True
)
try:
handle = device.open()
try:
serial_number = handle.getASCIIStringDescriptor(
handle.getDevice().getSerialNumberDescriptor()
)
print("SerialNumber", serial_number)
except usb1.USBError as e:
print(e, " -> Looking for SerialNumber Descriptor")
so after hacking around in the dark I got the above to work but I still have an issue
I have 2 Arduino's
can I use getDeviceIterator
and if so how?
many thanks
ps. libusb1.0 documentation => libusb.info
can I use
getDeviceIterator
and if so how?
Scavenging your original code, here is how I would do it in a nutshell:
with usb1.USBContext() as context:
for device in context.getDeviceIterator(skip_on_error=True):
try:
print(
"ID %04x:%04x" % (device.getVendorID(), device.getProductID()),
"->".join(
str(x)
for x in ["Bus %03i" % (device.getBusNumber(),)]
+ device.getPortNumberList()
),
"Device",
device.getDeviceAddress(),
"SerialNumber",
device.getSerialNumber(),
)
except usb1.USBError:
continue
You may want to restrict try
scope to only retrieving the descriptors, and print once you have all the values you need. It could make a difference in code which does not just print, but here it should be equivalent.
Thanks @vpelletier
you need to bang your drum about v1.6.6
as you are the only current python package that can supply Serial_Number with out opening a handle
much appreciated, keep up the good work
@whitequark
It is tempting to use the API in what appears the easiest possible way
Indeed, and closest-available-method would certainly qualify (foo.getBar()
rather than foo.getBoo().getBar()
).
I thought a bit more about this, and it now feels natural to me to have:
USBDevice
with only pre-open
methods (plus open
itself), so device/manufacturer/serial strings do not really belong thereUSBDeviceHandle
with all methods requiring device to be opened (plus getDevice
to not require code using python-libudeb1 to carry both instances around at all times). So the above 3 belong here just as much as getConfiguration
: while they are closely related to descriptors, they anyway require talking to the device.So I went this way in 023344d3f86d1067a1fc56124cb105cf075f5612 .
@vpelletier Great, thanks!
WinUSB always opens an USB device in exclusive mode. This means that you can't do something like...
because
USBDevice.getSerialNumber()
opens the device a second time internally and that fails withLIBUSB_ERROR_ACCESS
.I think
getDevice()
should return a "pre-opened"USBDevice
object to avoid this.