Usb Automatic Detection in Python for Linux Env

Obtain device path from pyudev with python

Device(u'/sys/devices/pci0000:00/pci0000:00:01.0/0000.000/usb1/1-2') is a USB device (i.e. device.device_type == 'usb_device'). At the time of its enumeration the /dev/tty* file does not exist yet as it gets assigned to its child USB interface later during its own enumeration. So you need to wait for a separate device added event for the Device(u'/sys/devices/pci0000:00/pci0000:00:01.0/0000.000/usb1/1-2:1.0') which would have device.device_type == 'usb_interface'.

Then you could just do print [os.path.join('/dev', f) for f in os.listdir(device.sys_path) if f.startswith('tty')] in its device_added():

import os
import glib
import pyudev
import pyudev.glib

context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by(subsystem='usb')
observer = pyudev.glib.GUDevMonitorObserver(monitor)

def device_added(observer, device):
if device.device_type == "usb_interface":
print device.sys_path, [os.path.join('/dev', f) for f in os.listdir(device.sys_path) if f.startswith('tty')]

observer.connect('device-added', device_added)
monitor.start()

mainloop = glib.MainLoop()
mainloop.run()

Linux and Python: auto-detect Arduino serial port

First of all, if you're using a shell, you can use a glob (*), so your command would become ls /dev/tty.usbmodem*.

Next, you don't even have to call a shell command to use a glob in Python!

Consider the following code:

import glob

print(glob.glob("/dev/tty.usbmodem*"))

Detect inserted USB on Windows

Yes, you need to use the RegisterDeviceNotification Windows API call. As far as I know, there is no Python module that wraps this functionality, so you have to use ctypes to call this function.

Fortunately, you are not the first person who has wanted to do this, so there are some code samples floating around the web. WxPython provides a code sample, but as you are writing a daemon this may not interest you. You might want to try the following code sample, which relies on both ctypes and pywin32, lifted shameless from Tim Golden:

import win32serviceutil
import win32service
import win32event
import servicemanager

import win32gui
import win32gui_struct
struct = win32gui_struct.struct
pywintypes = win32gui_struct.pywintypes
import win32con

GUID_DEVINTERFACE_USB_DEVICE = "{A5DCBF10-6530-11D2-901F-00C04FB951ED}"
DBT_DEVICEARRIVAL = 0x8000
DBT_DEVICEREMOVECOMPLETE = 0x8004

import ctypes

#
# Cut-down clone of UnpackDEV_BROADCAST from win32gui_struct, to be
# used for monkey-patching said module with correct handling
# of the "name" param of DBT_DEVTYPE_DEVICEINTERFACE
#
def _UnpackDEV_BROADCAST (lparam):
if lparam == 0: return None
hdr_format = "iii"
hdr_size = struct.calcsize (hdr_format)
hdr_buf = win32gui.PyGetMemory (lparam, hdr_size)
size, devtype, reserved = struct.unpack ("iii", hdr_buf)
# Due to x64 alignment issues, we need to use the full format string over
# the entire buffer. ie, on x64:
# calcsize('iiiP') != calcsize('iii')+calcsize('P')
buf = win32gui.PyGetMemory (lparam, size)

extra = {}
if devtype == win32con.DBT_DEVTYP_DEVICEINTERFACE:
fmt = hdr_format + "16s"
_, _, _, guid_bytes = struct.unpack (fmt, buf[:struct.calcsize(fmt)])
extra['classguid'] = pywintypes.IID (guid_bytes, True)
extra['name'] = ctypes.wstring_at (lparam + struct.calcsize(fmt))
else:
raise NotImplementedError("unknown device type %d" % (devtype,))
return win32gui_struct.DEV_BROADCAST_INFO(devtype, **extra)
win32gui_struct.UnpackDEV_BROADCAST = _UnpackDEV_BROADCAST

class DeviceEventService (win32serviceutil.ServiceFramework):

_svc_name_ = "DevEventHandler"
_svc_display_name_ = "Device Event Handler"
_svc_description_ = "Handle device notification events"

def __init__(self, args):
win32serviceutil.ServiceFramework.__init__ (self, args)
self.hWaitStop = win32event.CreateEvent (None, 0, 0, None)
#
# Specify that we're interested in device interface
# events for USB devices
#
filter = win32gui_struct.PackDEV_BROADCAST_DEVICEINTERFACE (
GUID_DEVINTERFACE_USB_DEVICE
)
self.hDevNotify = win32gui.RegisterDeviceNotification (
self.ssh, # copy of the service status handle
filter,
win32con.DEVICE_NOTIFY_SERVICE_HANDLE
)

#
# Add to the list of controls already handled by the underlying
# ServiceFramework class. We're only interested in device events
#
def GetAcceptedControls(self):
rc = win32serviceutil.ServiceFramework.GetAcceptedControls (self)
rc |= win32service.SERVICE_CONTROL_DEVICEEVENT
return rc

#
# Handle non-standard service events (including our device broadcasts)
# by logging to the Application event log
#
def SvcOtherEx(self, control, event_type, data):
if control == win32service.SERVICE_CONTROL_DEVICEEVENT:
info = win32gui_struct.UnpackDEV_BROADCAST(data)
#
# This is the key bit here where you'll presumably
# do something other than log the event. Perhaps pulse
# a named event or write to a secure pipe etc. etc.
#
if event_type == DBT_DEVICEARRIVAL:
servicemanager.LogMsg (
servicemanager.EVENTLOG_INFORMATION_TYPE,
0xF000,
("Device %s arrived" % info.name, '')
)
elif event_type == DBT_DEVICEREMOVECOMPLETE:
servicemanager.LogMsg (
servicemanager.EVENTLOG_INFORMATION_TYPE,
0xF000,
("Device %s removed" % info.name, '')
)

#
# Standard stuff for stopping and running service; nothing
# specific to device notifications
#
def SvcStop(self):
self.ReportServiceStatus (win32service.SERVICE_STOP_PENDING)
win32event.SetEvent (self.hWaitStop)

def SvcDoRun(self):
win32event.WaitForSingleObject (self.hWaitStop, win32event.INFINITE)
servicemanager.LogMsg (
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, '')
)

if __name__=='__main__':
win32serviceutil.HandleCommandLine (DeviceEventService)

Identifying serial/usb device python

No matter how you configure your device, at some point you're probably going to have to ask the user where the port is, or poll all serial devices for a known response. (Polling has it's pitfalls though, so read on!). Unlike USB devices, there is no vendor/device ID that is made known to the OS when you attach a plain-old serial device.

First you need to find the serial ports. Here's a question that might help: What is the cross-platform method of enumerating serial ports in Python (including virtual ports)?.

Once you have a list of serial ports, you could ask the user whether they know which one to use. If they do, problem solved!

If they don't, you could offer to poll ALL serial devices with some data that you know will yield a certain response from your device. Keep in mind though that if the user has other serial devices attached, your string of "hello" bytes might actually be the self-destruct sequence for some other device! Hence, you should warn the user that polling may interfere with other devices, and always prompt them before you do so.

Without knowing more about your code (eg. what comms framework, if any, you're using; are you doing this in the console or are you using a GUI toolkit, etc), it's impossible to say what the best way to code this might be. In the simplest case, you could just loop over all of your serial devices, send the greeting and check for a response. (You could also do this in parallel: loop once for the greeting, and loop again to check what's in the buffer. If you're going to get more fancy than that, use a proper library.)


Side note: You might be able to get around this if you have a built-in converter that you can set the vendor/device ID for, but the converter will still be automatically detected by any modern OS and enumerated as a serial port; you won't get to talk to it directly as a USB device. It could be possible to figure out which port goes with which ID, but I've never tried to do that. But this approach is useless if you're not the one who gets to pick the converter (eg. if it's a user-supplied cable).

USBTMC in Python

@Olaf, you were right in asking. It is to be used as a serial device using PySerial. Here is how it worked, the problems I faced, and their solution.

  1. I connected the device through the USB cable, and did lsusb:

    root@pelcon:~# lsusb

    Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
    Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 001 Device 004: ID 0bda:0129 Realtek Semiconductor Corp. RTS5129 Card Reader Controller
    Bus 001 Device 003: ID 0cf3:3004 Atheros Communications, Inc.
    Bus 001 Device 006: ID 0403:b972 Future Technology Devices International, Ltd
    Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
    Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 003 Device 002: ID 174f:114f Syntek
    Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub`
  2. Next, I checked for detection of the device as:

    root@pelcon:~# ls -al /dev/ttyUSB*

    ls: cannot access /dev/ttyUSB**: No such file or directory
  3. Then I looked into this matter and found "In recent kernels (definitely with 14.04 LTS), the ftdi_sio module no longer accepts the product and vendor options." in Can't open port /dev/ttyUSB0.

  4. I also checked dmesg | tail which showed me that FTDI USB was detected, but it was disconnected.

  5. Then I did two things:

    a. Created /etc/udev/rules.d/99-axe027.rules with contents ATTR{idProduct}=="b972", ATTR{idVendor}=="0403", RUN+="/sbin/modprobe -q ftdi_sio product=0xb972 vendor=0x0403". I restarted and again did dmesg | tail. But it was still undetected. Steps are given in Can't program with USB adapter except through sudo nautilus ubuntu

    b. Then I searched further, and this time I found Attaching USB-Serial device with custom PID to ttyUSB0 on embedded, where I did:

    1. Unplugged the device

    2. modprobe ftdi_sio

    3. echo 0403 b972 >/sys/bus/usb-serial/drivers/ftdi_sio/new_id

    4. Plugged in the device

    5.

    ls -al /dev/ttyUSB*

    crw-rw---- 1 root usbtmc 188, 0 Jul 23 11:33 /dev/ttyUSB0

    6.

    dmesg | tail
    [ 1162.348082] usb 1-1.2: Product: 6000A Phase Meter
    [ 1162.348086] usb 1-1.2: Manufacturer: clarke-hess
    [ 1162.348089] usb 1-1.2: SerialNumber: 187
    [ 1162.350801] ftdi_sio 1-1.2:1.0: FTDI USB Serial Device converter detected
    [ 1162.350837] usb 1-1.2: Detected FT232BM
    [ 1162.350840] usb 1-1.2: Number of endpoints 2
    [ 1162.350842] usb 1-1.2: Endpoint 1 MaxPacketSize 64
    [ 1162.350844] usb 1-1.2: Endpoint 2 MaxPacketSize 64
    [ 1162.350846] usb 1-1.2: Setting MaxPacketSize 64
    [ 1162.351284] usb 1-1.2: FTDI USB Serial Device converter now attached to ttyUSB0`

So, this way my device was detected, and I was able to communicate to it via PySerial as:

import serial, time
se = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)
print('Open USB serial connection:')
print '\t', se

print se.portstr # Confirm which port was really used
se.write("*IDN?\n")
data = se.readline()
time.sleep(2)
print data
se.close()

which gave the output as:

Open USB serial connection:

Serial<id=0xb329574c, open=True>(port='/dev/ttyUSB0', baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1, xonxoff=False, rtscts=False, dsrdtr=False)

/dev/ttyUSB0

CLARKE-HESS,6000A,187,1.07

How to execute a shellscript when I plug-in a USB-device

If you want to run the script on a specific device, you can use the vendor and product ids

  • In /etc/udev/rules.d/test.rules:

    ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", RUN+="/tmp/test.sh"
  • in test.sh:

    #! /bin/sh

    env >>/tmp/test.log
    file "/sys${DEVPATH}" >>/tmp/test.log

    if [ "${ACTION}" = add -a -d "/sys${DEVPATH}" ]; then
    echo "add ${DEVPATH}" >>/tmp/test.log
    fi

With env, you can see what environment is set from udev and with file, you will discover the file type.

The concrete attributes for your device can be discovered with lsusb

lsusb

gives

...

Bus 001 Device 016: ID 152d:2329 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 SATA Bridge

...



Related Topics



Leave a reply



Submit