Listado de puertos serie (COM) en Windows?
-
05-07-2019 - |
Pregunta
Estoy buscando una forma sólida de enumerar los puertos serie (COM) disponibles en una máquina con Windows. Hay esta publicación sobre el uso de WMI , pero me gustaría algo menos específico de .NET - Quiero obtener la lista de puertos en un programa Python o C ++, sin .NET.
Actualmente, conozco otros dos enfoques:
-
Leyendo la información en la clave de registro
HARDWARE \\ DEVICEMAP \\ SERIALCOMM
. Esto parece una gran opción, pero ¿es robusto ? No puedo encontrar una garantía en línea o en MSDN de que esta celda de registro tenga la lista completa de puertos disponibles. -
Intente llamar a
CreateFile
enCOMN
con N un número de 1 a algo. Esto no es lo suficientemente bueno, porque algunos puertos COM no se denominan COMN. Por ejemplo, algunos puertos COM virtuales creados se denominan CSNA0, CSNB0, etc., por lo que no confiaría en este método.
¿Algún otro método / idea / experiencia para compartir?
Editar: por cierto, aquí hay una implementación sencilla de Python para leer los nombres de puertos del registro:
import _winreg as winreg
import itertools
def enumerate_serial_ports():
""" Uses the Win32 registry to return a iterator of serial
(COM) ports existing on this computer.
"""
path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
except WindowsError:
raise IterationError
for i in itertools.count():
try:
val = winreg.EnumValue(key, i)
yield (str(val[1]), str(val[0]))
except EnvironmentError:
break
Solución
Hay varias opciones disponibles:
-
Llame a QueryDosDevice con una NULL lpDeviceName para enumerar todos los dispositivos DOS. Luego use CreateFile y GetCommConfig con cada nombre de dispositivo en diríjase a averiguar si se trata de un puerto serie.
-
Llame a SetupDiGetClassDevs con un ClassGuid de GUID_DEVINTERFACE_COMPORT.
Hay una conversación en el win32 newsgroup y un CodeProject, er, proyecto .
Otros consejos
Usando pySerial con Python:
import serial.tools.list_ports
ports = list(serial.tools.list_ports.comports())
for p in ports:
print p
El proyecto PySerial proporciona un par de soluciones .
Acabo de crear lo siguiente, basado en la lectura a través de la fuente de C ++ en EnumSerialPorts y al ver el función GetDefaultCommConfig ()
. Parecía el método más simple utilizando ANSI C simple y una única llamada a la API para cada puerto COM posible.
#include <stdio.h>
#include <windows.h>
#include <winbase.h>
BOOL COM_exists( int port)
{
char buffer[7];
COMMCONFIG CommConfig;
DWORD size;
if (! (1 <= port && port <= 255))
{
return FALSE;
}
snprintf( buffer, sizeof buffer, "COM%d", port);
size = sizeof CommConfig;
// COM port exists if GetDefaultCommConfig returns TRUE
// or changes <size> to indicate COMMCONFIG buffer too small.
return (GetDefaultCommConfig( buffer, &CommConfig, &size)
|| size > sizeof CommConfig);
}
int main()
{
int i;
for (i = 1; i < 256; ++i)
{
if (COM_exists( i))
{
printf( "COM%d exists\n", i);
}
}
return 0;
}
Esto definitivamente es bastante tarde, ¡pero me resultó útil!
http: / /eli.thegreenplace.net/2009/07/31/listing-all-serial-ports-on-windows-with-python/
Particularmente este ejemplo:
import re
def full_port_name(portname):
""" Given a port-name (of the form COM7,
COM12, CNCA0, etc.) returns a full
name suitable for opening with the
Serial class.
"""
m = re.match('^COM(\d+), portname)
if m and int(m.group(1)) < 10:
return portname
return '\\\\.\\' + portname
Hay un ejemplo en la distribución de pyserial ahora que hace esto llamado scanwin32.py
http://pyserial.sourcearchive.com/documentation/2.5/scanwin32_8py_source.html
Creo que WMI es el camino a seguir, ya que es bastante fácil de usar y tiene un código mínimo. Le ahorra tener que excavar dentro del registro y le da alguna garantía de que funcionará para situaciones más generales en el futuro.
Puede instalar todo lo necesario con pip install pypiwin32 WMI
y funciona de manera inmediata.
Código
import wmi
query = "SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%)'"
coms = wmi.WMI().query(query)
for com in coms:
print(com.Name)
Output
Communications Port (COM1)
mbed Serial Port (COM3)
mbed Serial Port (COM5)
Supongo que su puerto serie es una especie de Plug 'n Play, por lo que debería funcionar bien. Por alguna razón, Win32_SerialPort
no funciona para todos los puertos.
Hoy en día hay una sola línea de Powershell para eso.
[System.IO.Ports.SerialPort]::GetPortNames()