dhylands/usb-ser-mon

import and attribute errors Mac OS Mojave 10.14.6 Python 3.7.4

tcarroll2 opened this issue · 9 comments

I installed your package via pip and upon running I get this error:

Traceback (most recent call last):
File "/Users/carroll/.vscode/extensions/ms-python.python-2019.8.30787/pythonFiles/ptvsd_launcher.py", line 43, in
main(ptvsdArgs)
File "/Users/carroll/.vscode/extensions/ms-python.python-2019.8.30787/pythonFiles/lib/python/ptvsd/main.py", line 432, in main
run()
File "/Users/carroll/.vscode/extensions/ms-python.python-2019.8.30787/pythonFiles/lib/python/ptvsd/main.py", line 316, in run_file
runpy.run_path(target, run_name='main')
File "/Users/carroll/.pyenv/versions/3.7.4/lib/python3.7/runpy.py", line 263, in run_path
pkg_name=pkg_name, script_name=fname)
File "/Users/carroll/.pyenv/versions/3.7.4/lib/python3.7/runpy.py", line 96, in _run_module_code
mod_name, mod_spec, pkg_name, script_name)
File "/Users/carroll/.pyenv/versions/3.7.4/lib/python3.7/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/Users/Shared/toms-music-bingo/test-tty.py", line 145, in
main()
File "/Users/Shared/toms-music-bingo/test-tty.py", line 136, in main
context = pyudev.Context()
File "/Users/carroll/.pyenv/versions/3.7.4/lib/python3.7/site-packages/pyudev/core.py", line 64, in init
self._libudev = load_ctypes_library('udev', SIGNATURES, ERROR_CHECKERS)
File "/Users/carroll/.pyenv/versions/3.7.4/lib/python3.7/site-packages/pyudev/_ctypeslib/utils.py", line 56, in load_ctypes_library
raise ImportError('No library named %s' % name)
ImportError: No library named udev
Exception ignored in: <function Context.del at 0x106b8e4d0>
Traceback (most recent call last):
File "/Users/carroll/.pyenv/versions/3.7.4/lib/python3.7/site-packages/pyudev/core.py", line 68, in del
self._libudev.udev_unref(self)
AttributeError: 'Context' object has no attribute '_libudev'

I am on Mac OS Mojave 10.14.6 using Python version 3.7.4. All I am trying to do is determine which /dev/ttys my barcode/qr code scanner is connected to.

Thank you!

Unfortunately, the way usb-ser-mon is coded it relies on udev, which is only available under linux.

In order to determine which /dev/ttys your barcode scanner is connected to, try using this utility:
https://github.com/dhylands/dotfiles/blob/master/bin/find_port.py

Run it with the -l option. It only uses python serial port and no depdendencies on udev. I designed it to be run from within shell scripts for find serial ports. It can search based on vendor name, PID, VID, and serial number.

I tried running the find_port.py script, but firstly I had to comment out the sys.exit(1) line near the bottom as Python was not liking it. I was reading the docs here, but I could see why that wouldn't work.

Anyway, I ran your script and it failed to detect my scanner on the ttys* port.

I can use pyusb along with libusb1 as the backend and it sees the device connected, but it does not tell me which ttys* port it is connected to, which I find strange. Here's the code I am using with pyusb:

import usb
import usb.backend.libusb1 as libusb1
import libusb1 as libusb

# find our device
dev = usb.core.find(idVendor=0xad93, idProduct=0x4002, backend=libusb1.get_backend())

print (dev)

This is the output when I run the above code:

DEVICE ID ad93:4002 on Bus 020 Address 001 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x110 USB 1.1
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0xad93
 idProduct              : 0x4002
 bcdDevice              :  0x100 Device 1.0
 iManufacturer          :    0x1 YK
 iProduct               :    0x2 YK-2D PRODUCT HID KBW
 iSerialNumber          :    0x3 APP-000000000
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 200 mA ==================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x22 (34 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0 
   bmAttributes         :   0x80 Bus Powered
   bMaxPower            :   0x64 (200 mA)
    INTERFACE 0: Human Interface Device ====================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x1
     bInterfaceClass    :    0x3 Human Interface Device
     bInterfaceSubClass :    0x1
     bInterfaceProtocol :    0x1
     iInterface         :    0x0 
      ENDPOINT 0x81: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :    0x8 (8 bytes)
       bInterval        :    0x1

This would be all I need if only it would show the ttys* port it is reading this information from. Would the ttys* port be one of the 0x** codes above?

Thank you for your response and help.

It looks like your scanner is presenting itself as a HID device and not a USB Serial device (which is why it's not showing up with a tty. If it was showing up as a tty then you should be able to do

ls /dev/tty.*

On my Mac I see something like this with a usb serail device plugged in:

508 >ls /dev/tty.*
/dev/tty.Bluetooth-Incoming-Port  /dev/tty.MALS                     /dev/tty.SOC                      /dev/tty.usbserial-1440

Notice the /dev/tty.usbserial-1440 on the far right (you'll probably need to scroll the display).

If I run your little python program then I see (The Digi dongle is using an FTDI USB-to-serial chip)

DEVICE ID 0403:6001 on Bus 020 Address 002 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :    0x0 Specified at interface
 bDeviceSubClass        :    0x0
 bDeviceProtocol        :    0x0
 bMaxPacketSize0        :    0x8 (8 bytes)
 idVendor               : 0x0403
 idProduct              : 0x6001
 bcdDevice              :  0x600 Device 6.0
 iManufacturer          :    0x1 Digi
 iProduct               :    0x2 XStick
 iSerialNumber          :    0x0
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 100 mA ==================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :   0x20 (32 bytes)
   bNumInterfaces       :    0x1
   bConfigurationValue  :    0x1
   iConfiguration       :    0x0
   bmAttributes         :   0x80 Bus Powered
   bMaxPower            :   0x32 (100 mA)
    INTERFACE 0: Vendor Specific ===========================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x2
     bInterfaceClass    :   0xff Vendor Specific
     bInterfaceSubClass :   0xff
     bInterfaceProtocol :   0xff
     iInterface         :    0x2 XStick
      ENDPOINT 0x81: Bulk IN ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0
      ENDPOINT 0x2: Bulk OUT ===============================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x2 OUT
       bmAttributes     :    0x2 Bulk
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x0

When my barcode scanner is plugged in, it comes up as ttys002 most of the time. It could change from that to ttys003 or another number at times, or if I plug it into my macbook it can be different. So that is why I am trying to figure out how to identify which /dev/ device it is being represented as.

Is there a way to poll HID devices since it is presenting itself as an HID device?

When I first got the scanner and plugged it in Mac OS recognized it as a keyboard and wanted me to hold down the shift key. I just canceled that setup process.

The scanner works, but I have to ensure my input field has focus, or it won't capture the barcode when I scan it. I would like to trap the code without having to have the input field having focus. But, for now, I can live with it until I need to do something. I have a thread set up to continuously read the /dev/ttys002 port, but it doesn't capture anything when a barcode is scanned. Here's my thread function:

def wait_for_qrcode():
    global stopGame
    checkWinEntry.delete(0, END)
    checkWinEntry.focus_set()
    f=open("/dev/ttys002")
    while len(checkWinEntry.get()) == 0 and not stopGame:
        qrcode = f.read()
        time.sleep(1)
    f.close
    print (qrcode)
    check_win(qrcode)

But this code would only work if I knew for certain the /dev/ttys002 was correct.

Again, thanks for your help.

Since its acting like a keyboard, then it makes sense that its coming up as a HID device.

I know under linux there are "input" drivers and you can setup your code to read from each individual input device (keyboard, mouse, etc), but I'm not that familiar with the Mac, so I'm not sure.

So went digging a little more with the HID info, and I found pyhid and libusb/hidapi. I installed it and I can see the device using their code:

import hid

# get list of HID devices connected to this computer
h=hid.enumerate()
print ("HID info=", h,"\n")

# get path of first item
item=0
path=h[item]['path']
vid=h[item]['vendor_id']
pid=h[item]['product_id']

print ("Path=%s Vid=%s Pid=%s" % (path, vid, pid) + "/n")

# open the device
try:

    # using path works on raw, not non-raw
    #    d=hid.Device(path=path)
    #    print "Opened device=", path

    d=hid.Device(vid=vid, pid=pid)
    print ("Opened device=", vid, pid, "\n")

    # -- TBD
    #r=d.get_feature_report(1, 1024)
    #print "Report=", r

    # loop rading the keyboard....
    done=0
    while(done==0):
        r=d.read(10)
        for c in r:
            x=ord(c)
            print ("read=",x,"\n")
        
except Exception as e:
    print ("Exception, e=", e)

print ("Done!")

Which results in a bunch of information, but here's the relevant entry for the barcode scanner:

{'path': b'IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/XHC1@14/XHC1@14000000/HS10@14600000/YK-2D PRODUCT HID KBW@14600000/IOUSBHostInterface@0/IOUSBHostHIDDevice@14600000,0', 'vendor_id': 44435, 'product_id': 16386, 'serial_number': 'APP-000000000', 'release_number': 256, 'manufacturer_string': 'YK', 'product_string': 'YK-2D PRODUCT HID KBW', 'usage_page': 1, 'usage': 6, 'interface_number': 0}

Path=b'IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/AppleHSBluetoothDevice/Accelerometer@2/AppleHSBluetoothHIDDriver' Vid=76 Pid=617/n
Opened device= 76 617 

Still doesn't give me the /dev/ttys path that it is. This time when I plugged it in, it is coming up as /dev/ttys003, I think.

As you can see from the directory listing, ttys001 through ttys003 are all active at nearly the same time.

crw--w----  1 carroll  tty     16,   0 Aug 29 23:04 /dev/ttys000
crw--w----  1 carroll  tty     16,   1 Aug 30 14:40 /dev/ttys001
crw--w----  1 carroll  tty     16,   2 Aug 30 14:03 /dev/ttys002
crw--w----  1 carroll  tty     16,   3 Aug 30 14:30 /dev/ttys003
crw-rw-rw-  1 root     wheel    4,  49 Aug 27 23:14 /dev/ttys1
crw-rw-rw-  1 root     wheel    4,  50 Aug 27 23:14 /dev/ttys2
crw-rw-rw-  1 root     wheel    4,  51 Aug 27 23:14 /dev/ttys3
crw-rw-rw-  1 root     wheel    4,  52 Aug 27 23:14 /dev/ttys4
crw-rw-rw-  1 root     wheel    4,  53 Aug 27 23:14 /dev/ttys5
crw-rw-rw-  1 root     wheel    4,  54 Aug 27 23:14 /dev/ttys6
crw-rw-rw-  1 root     wheel    4,  55 Aug 27 23:14 /dev/ttys7
crw-rw-rw-  1 root     wheel    4,  56 Aug 27 23:14 /dev/ttys8
crw-rw-rw-  1 root     wheel    4,  57 Aug 27 23:14 /dev/ttys9
crw-rw-rw-  1 root     wheel    4,  58 Aug 27 23:14 /dev/ttysa
crw-rw-rw-  1 root     wheel    4,  59 Aug 27 23:14 /dev/ttysb
crw-rw-rw-  1 root     wheel    4,  60 Aug 27 23:14 /dev/ttysc
crw-rw-rw-  1 root     wheel    4,  61 Aug 27 23:14 /dev/ttysd
crw-rw-rw-  1 root     wheel    4,  62 Aug 27 23:14 /dev/ttyse
crw-rw-rw-  1 root     wheel    4,  63 Aug 27 23:14 /dev/ttysf

It looks like the /dev/ttysXXX are psuedo tty's. If you open a terminal window and type the tty command then it will show you the /dev/ttysXXX entry that's been assigned to that particular terminal.

The command lsof | grep ttys0 (takes a few minutes) will tell you which processes have which /dev/ttysXXX assigned to them.

I suspect that if you knew the tty for the terminal that launched your "reader" process and that terminal had focus then it would get the characters from the scanner. But as soon as you change the focus away, it will stop receiving the scanner data.