I have an 2-port signal relay connected to my computer via a USB serial interface. Using the pyserial module I can control these relays with ease. However, this is based on the assumption that I know beforehand which COM-port (or /dev-node) the device is assigned to.
For the project I'm doing that's not enough since I don't want to assume that the device always gets assigned to for example COM7 in Windows. I need to be able to identify the device programatically across the possible platforms (Win, Linux, OSX (which I imagine would be similar to the Linux approach)), using python. Perhaps by, as the title suggests, enumerate USB-devices on the system and somehow get more friendly names for them. Windows and Linux being the most important platforms to support.
Any help would be greatly appreciated!
EDIT:
Seems like the pyudev-module would be a good fit for Linux-systems. Has anyone had any experience with that?
Regarding Linux, if all you need is to enumerate devices, you can even skip pyudev dependency for your project, and simply parse the output of /sbin/udevadm info --export-db command (does not require root privileges). It will dump all information about present devices and classes, including USB product IDs for USB devices, which should be more then enough to identify your USB-to-serial adapters. Of course, you can also do this with pyudev.
I know this is an older post, but I was struggling with it today.
Ultimately I used the wmi library for python as I'm on a Windows machine (sorry, I know my answer only applies to Windows, but maybe it'll help someone).
Install the package using pip first:
pip install wmi
then
import wmi
c = wmi.WMI()
wql = "Select * From Win32_USBControllerDevice"
for item in c.query(wql):
print item.Dependent.Caption
Should result with something like:
USB Root Hub
USB Root Hub
Prolific USB-to-Serial Comm Port (COM9)
USB Root Hub
USB Root Hub
USB Composite Device
USB Video Device
USB Audio Device
USB Root Hub
...snip...
In this case, you'd have to string parse the Caption to find the COM port. You can also take a look at just the item. Dependent object to see other attributes of the USB device beside Caption that you may find relevant:
instance of Win32_PnPEntity
{
Caption = "USB Root Hub";
ClassGuid = "{36fc9e60-c465-11cf-8056-444553540000}";
ConfigManagerErrorCode = 0;
ConfigManagerUserConfig = FALSE;
CreationClassName = "Win32_PnPEntity";
Description = "USB Root Hub";
DeviceID = "USB\\ROOT_HUB\\4&32F13EF0&1";
HardwareID = {"USB\\ROOT_HUB&VID8086&PID3A36&REV0000",
"USB\\ROOT_HUB&VID8086&PID3A36", "USB\\ROOT_HUB"};
Manufacturer = "(Standard USB Host Controller)";
Name = "USB Root Hub";
PNPDeviceID = "USB\\ROOT_HUB\\4&32F13EF0&1";
Service = "usbhub";
Status = "OK";
SystemCreationClassName = "Win32_ComputerSystem";
SystemName = "001fbc0934d1";
};
At least for linux, you can use some dummy hacks to determine your /dev node, by inspecting for example the output of "ls /dev | grep ttyUSB" before and after you attach your device. This somehow must apply as well for the OSX case. A good idea is to inspect those commands using something like the subprocess.Popen() command. As for windows, this might be helpful.
Windows: you can pull USB information from WMI, but you need to be administrator. The examples are in .NET, but you should be able to use the Python WMI module. This will give you access to USB identification strings, which may contain useful information. For FTDI serial devices there is a short cut using FTDI's DLL, which does not require privileged access.
Linux: all the available information is under /sys/bus/usb, and also available through udev. This looks like a good answer.
As far as Windows goes, you could scan the registry:
import _winreg as reg
from itertools import count
key = reg.OpenKey(reg.HKEY_LOCAL_MACHINE, 'HARDWARE\\DEVICEMAP\\SERIALCOMM')
try:
for i in count():
device, port = reg.EnumValue(key, i)[:2]
print device, port
except WindowsError:
pass
It will be great if this is possible, but in my experience with commercial equipments using COM ports this is not the case. Most of the times you need to set manually in the software the COM port. This is a mess, specially in windows (at least XP) that tends to change the number of the com ports in certain cases. In some equipment there is an autodiscovery feature in the software that sends a small message to every COM port and waits for the right answer. This of course only works if the instrument implements some kind of identification command. Good luck.
Related
I have 2 identical USB-to-serial adapter cables (pretty much like this one) to connect a desktop PC under Ubuntu and some RS232 devices.
I develop a python software to pilot these devices.
I need to find a way to identify which one of the 2 adapters I am connected to.
I know about python's serial.tools.list_ports.comports() function, but all the settings are the exact same for both adapters (see capture below). Except the device, but it may change depending on the plugging order.
How can I change some settings' field to make both adapter pythonically distinguishable? Is it possible to write my own serial_number for example?
To solve this problem there are some alternative paths to serial devices in Linux.
There's either
/dev/serial/by-id/ and /dev/serial/by-path
variant to access your devices.
If this is always for a specific serial device, the normal way to do this is to use the udev program to create symlinks for you in /dev. There's a lot of different options for how to do this, either based on what physical port it's plugged into or based off of attributes of the device(e.g. serial number).
FTDI based devices all have a serial number associated with them, but since yours is a Prolific they don't have a serial number, so this becomes a bit harder. However, since we can use udev to create a symlink based on where it is plugged in, something like the following should work(put this in a file seen by udev, e.g. /etc/udev/rules.d/80-my-converter.rules):
SUBSYSTEM=="usb", KERNELS=="2-1.8.3", SYMLINK+="device_1"
SUBSYSTEM=="usb", KERNELS=="2-1.8.1.3", SYMLINK+="device_2"
The KERNELS parameter will have to change depending on where exactly you plug the serial device into and will be specific to your system. You can get a list of the udev parameters on your device by running the following:
udevadm info -a -n /dev/ttyUSB2
This page has more information on writing udev rules.
I have just tried to connect to usb mobile to send sms through it using AT commands. But when i use pyserial to connect to it in a windows os, i get error could not open port, the file specified cannot be found.
>>> import serial
>>> ser = serial.Serial(0) # open first serial port
>>> print ser.name # check which port was really used
>>> ser.write("hello") # write a string
>>> ser.close()
even if i replace the 0 with any other value, like 0 -10 or 'com0','com1' etc, i still get error file specified not found, port cannot be open.
There is a command listed in pyserial documentation which lists ports or allows you to open a port
python -m serial.tools.miniterm
This command is supposed to list all serial ports. But it shows none.
I have 3 usb ports on my system. What is causing this issue.
Pyserial is not guaranteed to detect all ports. This depends on how the device and the OS communicate and if pyserial is designed to pick up on this. For Example, pyserial could not detect a LabJack U3-LV or a EPSOM-POS receipt printer I plugged in in a Win8 session (both could be detected using a linux session).
Try serial.tools.list_ports.comports() and see what it gives you.
You could also look at the pyusb module for usb connections.
Also: I recommend being careful when using numbers for ports, as the mapping may not be "common-sense" logical (i.e. 0 may not map to COM0). Use explicit string names instead.
I found the solution myself. Its pretty simple and uses the same code in the question.. I was getting blocked error because there was no modem(nokia phone) in my device manager. although i had plugged in my phone into the usb, there was no nokia pc suite installed. Once you install nokia pc suite and connect your nokia phone, in device manager -> modems your phone will appear. Just check its properties->modem and you will find the com5 or whichever number. Then use that id to connect. If you dont know this, just iterate till you find the right one.
Thanks everyone who tried to help
I would like to know how to have a Python script determine how a computer is connected to a network. The reason for this is because the script becomes buggy if the connection speed is too slow, so I would like to show a warning if the user is connected via WiFi.
The actual Python executable being run is located on my company's LAN, so this is why the connection speed matters. Additionally, since this executable cannot be run without a connection, I am not concerned with checking that the user is online.
I know nothing about how Python interacts with things like this (or even if its possible), so please let me know if there's anything I can clarify. Thanks in advance
I am using Python 2.7.6 32-bit on Windows 7 64-bit (different bits because pyEnchant)
On Windows, this information is made available via WMI's Network Adapter Classes.
In .NET (e.g., with IronPython), this is easy to use:
ni = Windows.Networking.Connectivity.NetworkInformation
profile = ni.GetInternetConnectionProfile()
if profile:
interface = profile.NetworkAdapter.IanaInterfaceType
if interface == 71:
do WiFi stuff
elif interface == 6:
do Ethernet stuff
else:
do wtf stuff
The IanaInterfaceType is a number from this IANA list. (I you want to parse SNMP MIBs, you can download it that way.) Note that Windows may lump some less common/obsolete adapter types in with Ethernet, but you probably don't care about that.
For older versions of Windows—I'm not sure whether Windows 7 counts as "older" or not—you want Win32_NetworkAdapter and friends, not MSFT_NetworkAdapter/NetworkAdapter as linked above, so the code will be different.
In particular, getting the adapter that's supplying the default internet connection is harder, but more flexible, while getting its type is easier, but less informative. IIRC, at least in XP, even though Windows had some notion of "the internet connection", you couldn't access it through WMI—but you could access all internet connections, or search/iterate for the one you care about. Which is actually better if you've got a VPN, or are sitting on a DMZ, or have multiple NIC cards active at once, etc.
So, what you do is to search or iterate Win32_IP4RouteTable to find which connection route handles the IP address you actually want to connect to, or use Win32_NetworkAdapaterConfiguration to find which NIC handles the address you want to connect from. Either way, you will get an InterfaceIndex, and you can look up the Win32_NetworkAdapter with that InterfaceIndex. Then its AdapterType is a nice string that you can just check != "Wireless". (I believe I figured all of this out from a VBscript example at TechNet Script Center and then ported it to IronPython myself, but it was a long time ago, so I may be remembering wrong. Anyway, the docs seem more complete than they did back then, so that shouldn't be necessary.)
Without .NET (e.g., with standard CPython), you have to use either the web services interface, or talk to the native Win32 COM interface.
In theory, you could talk to the COM interface directly via ctypes, but that's a horrible idea; you almost certainly want pywin32 for that.
And, even better, I would suggest looking around on PyPI for wrappers, like this one, rather than building it yourself. Then, the code should be nearly as simple as with .NET.
Here's some pseudcode using the WMI library linked above and the XP-compatible source-address method described above:
import wmi
MY_IP = '10.1.2.3'
w = wmi.WMI()
for nac in w.Win32_NetworkAdapterConfiguration():
if MY_IP in nac.IPAddress:
idx = nac.InterfaceIndex
break
else:
oops, do something, no NIC has that address
na = w.Win32_NetworkAdapter(InterfaceIndex=idx)
if not na:
oops, unlikely race condition, NIC disabled right before we checked it
if na[0].AdapterType == 'Wireless':
do WiFi stuff
elif na[0].AdapterType.startswith('Ethernet'):
do Ethernet stuff
else:
who's using LocalTalk in 2013?
It should be possible to search the NetworkAddressConfiguration, but I have no idea how to search for "IPAddress contains MY_IP" in the wmi library. Using = might work there, who knows; read through the tutorial to find out.
Searching by target address, on the other hand, should be like this:
TARGET_IP = '10.10.10.10'
w = wmi.WMI()
rt = w.Win32_IP4RouteTable(Destination=TARGET_IP)
if not rt:
oops, no route to host
idx = rt[0].InterfaceIndex
# same as above
Using WMI you can determine the connection without needing the local machine IP.
Use NetConnectionID instead of AdapterType for comparing seems to work.
Works on Windows 10.
import wmi
w = wmi.WMI()
for nac in w.Win32_NetworkAdapterConfiguration():
if nac.IPEnabled:
idx = nac.InterfaceIndex
break
else:
print("oops, do something, no NIC has that address")
na = w.Win32_NetworkAdapter(InterfaceIndex=idx)
if not na:
print("oops, unlikely race condition, NIC disabled right before we checked it")
if 'Wi' in na[0].NetConnectionID:
print("do WiFi stuff")
elif 'Ethernet' in na[0].NetConnectionID:
print("do Ethernet stuff")
else:
print("who's using LocalTalk in 2013?")
I am using an RS232 Serial - USB Converter to connect to a controller. I am trying to connect to this port programmatically.
The device is getting listed as below when I give lsusb
Bus 003 Device 003: ID 05ad:0fba Y.C. Cable U.S.A., Inc.
But I am not able to see it's device path (something like /dev/ttyUSB*).
I would like to know the way to get the path from device id (or) a way to create / find the path which can be used to open the port from a program (Ruby, Python, C)
Centos has a fairly old kernel. It may be lacking in support for your device.
Are you able to try with a newer distribution (Fedora, Ubuntu, etc) to test whether it works or not? Failing that, do you have a different dongle you can use?
You can Google for "Centos 05ad:0fba" or similar combinations to find out whether it's supported.
I'm working on a utility that will auto mount an inserted USB stick on linux. I have tied into D-Bus to receive notification of when a device is inserted, and that works great. However, I need to determine which device in /dev is mapped to the inserted USB stick. I am getting the D-Bus notification and then scanning the USB system with pyUSB ( 0.4 ). I filter for USB_MASS_STORAGE_DEVICE classes, and I can see the device that's been added or removed. I need to mount this device so I can query it for available space and report that to our app so we can determine if enough free space exists so we can write our data.
I'm using python for this task. I'm not sure what our target distro will be, only that it will be at least 2.6
edit: My question is: How do I determine which device in /dev maps to the buss-device number I get from pyUSB.
You should probably ask HAL about that. You say you already get notifications from HAL by D-Bus... It maintains list of USB devices, together with their IDs and device names (block.device property).
Here's a nice example of how to get device file name together with the notification of new USB device: How can I listen for 'usb device inserted' events in Linux, in Python?
Why not use "os" module to mount the file system:
os.system ("mount ... ")
Or if you want to examine output use "popen":
l = op.popen ("mount ....").readlines()
what about using dmesg output to find out the device name (sdc1 etc...)
use it right after dbus tells you something is was inserted in USB. you could do tail dmesg for example