COM-Port Enumeration Linux/MacOS

#7527980
Lesenswert?

Hallo Foraner,

momentan schreibe ich eine Software, um über die serielle Schnittstelle eine Hardware zu Parametrisieren. Der Compiler (PureBasic, https://www.purebasic.com/german/) ist kein Cross-Compiler, und die Platformen Linux und MacOS stehen mir nicht zur Verfügung. Linux werde ich in eine VirtualBox installieren, für MacOS werde ich meine Nichte bitten, das zu kompilieren.

Problem: Zum Einstellen der seriellen Schnittstelle durchforste ich im Windows-Branch (Compiler-Select) einfach alle COM-Ports von 1...32 und stelle alle Gefundenen in einer ComboBox zur Auswahl bereit. Die Prozedur dafür ist:

1
Procedure testComPorts()
2
  anzahl = 0      
3
  Handshake = 0
4
  Buffer = 1024
5
  CompilerSelect #PB_Compiler_OS
6
    CompilerCase #PB_OS_Windows
7
      For i = 1 To 32
8
        ;Port.s = "COM"+ Str(i)
9
        Port.s = "\\\\.\\COM"+ Str(i)
10
        HCom = OpenSerialPort(0, Port, 9600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
11
        If HCom > 0
12
          AddGadgetItem(Combo_5, -1,"COM" + Str(i))
13
          CloseSerialPort(0)
14
          anzahl = anzahl + 1
15
        EndIf
16
      Next i
17
    CompilerCase #PB_OS_Linux
18
      ;Port.s = "/dev/ttyUSB0"
19
    CompilerCase #PB_OS_MacOS
20
      ;Port.s = "/dev/cu.usbserial-xxxxxxxx"
21
      ;         "/dev/tty.usbserial-xxxxxxxx"
22
  CompilerEndSelect
23
  If anzahl = 0
24
    MessageRequester("W A R N U N G", "Es wurde kein COM-Port gefunden",0) 
25
  EndIf
26
EndProcedure

Jetzt wollte ich für den Linux- und MacOS-Branch etwas ähnliches machen, bin aber nicht sicher, wie ich die Schnittstellen gelistet bekomme. Es kommen ausschliesslich FT232 zu Einsatz. Zu deren Enumeration habe ich gefunden: https://ftdichip.com/wp-content/uploads/2023/08/AN_552-Consistent-COM-Port-Enumeration-on-Linux.pdf und https://www.ftdichip.com/Support/Documents/InstallGuides/Mac_OS_X_Installation_Guide.pdf Kann ich bei Linux davon ausgehen, dass die Enumeration immer "/dev/ttyUSBx" ist? Im Beispiel aus dem Handbuch vom Compiler steht "/dev/ttySx". Bei MacOS wird ja die FTDI-Seriennummer nachgestellt, da darf man ja dann wirklich das /dev durchforsten. Hier wird das mit Linux gemacht: https://www.purebasic.fr/english/viewtopic.php?p=294057&sid=22f3d47c83264c33754c2ecfb54255f4#p294057 Ist das nicht ein bisschen zu extrem? Oder was ist Best Practice?

Als Workaround hätte ich die ComboBox editable gemacht, dann kann der User den Eintrag manuell machen. Kann ich davon ausgehen, dass ich einem Linux- oder Mac-User mehr zumuten kann? Die User sind Modelleisenbähnler.

Vielen Dank für Eure Tipps und Einschätzungen!

Gruss Chregu

Gast #7527991
Lesenswert?

Christian M. schrieb:

Kann ich bei Linux davon ausgehen, dass die Enumeration immer "/dev/ttyUSBx" ist?

Nein. /dev/ttyACMx ist auch gängig. Bei MacOS heißen sie wiederum anders. Letztendlich kann da jeder Treiber auch noch sein eigenes Süppchen kochen. Ein Programm dass sich auf ein bestimmtes Muster beschränkt, wird früher oder später Probleme bereiten. Ich würde zusätzlich zur Drop-Down Liste auch noch eine freie Texteingabe zulassen.

#7528003
Lesenswert?

Christian M. schrieb:

Problem: Zum Einstellen der seriellen Schnittstelle durchforste ich im Windows-Branch (Compiler-Select) einfach alle COM-Ports von 1...32 und stelle alle Gefundenen in einer ComboBox zur Auswahl bereit.

SO macht man das eigentlich nicht. Die Win32 API stellt zwei Wege für das Enumerieren von (beliebigen) Devices zur Verfügung:

  1. cfgmgr32
  2. setupapi

https://learn.microsoft.com/en-us/windows-hardware/drivers/install/enumerating-installed-device-interface-classes

Wenn Du Ports wahllos öffnest und wieder schließt, könnten das einige Geräte übelnehmen. Über die offiziellen APIs kannst Du auch USB-Devices durchenumerieren, nach bestimmten VID/PID/SerialNum filtern und dann abfragen, ob und wenn ja welcher COM: dann da dran hängt. Im Netz gibts genug Dokumentation dazu. Das hat den Vorteil, dass Du nur DEINE EIGENEN FTDI-UARTs bekommst und nicht irgendwelche fremden Geräte, die zufällig auch die gleichen FTDI-Bridges drin haben.

Bei Linux kannst Du entweder das sysfs durchiterieren (z.B. /sys/bus/usb/devices für alle USB-Devices oder /sys/class/tty für alle ttys), oder Du kannst über udev gehen.

Bei MacOS bin ich aktuell nicht im Bilde. Ich hab hier zwar einen stehen, aber auf dem mache ich keine Entwicklung.

Dein jetziger Weg ist jedenfalls nicht im Sinne der Erfinder.

fchk

#7528008
Lesenswert?

Danke Daniel! Würde dann das gehen:

1
dmesg = RunProgram("/bin/dmesg","","",#PB_Program_Open|#PB_Program_Read)
2
  fgrep = RunProgram("/bin/fgrep","tty","",#PB_Program_Open|#PB_Program_Connect|#PB_Program_Read,Dmesg)
3
  tee = RunProgram("/usr/bin/tee","tty.txt",GetCurrentDirectory(),#PB_Program_Open|#PB_Program_Connect|#PB_Program_Read,Fgrep)
4
  WaitProgram(tee)
5
  CloseProgram(Dmesg):CloseProgram(Fgrep):CloseProgram(Tee)

Also wie in der Konsole:

1
/bin/dmesg
2
/bin/fgrep tty
3
/usr/bin/tee tty.txt CurrentDirectory

um alle verfügbaren Devices in die Datei tty.txt zu schreiben?

Wie wäre was vergleichbares auf MacOS?

Gruss Chregu

#7528013
Lesenswert?

Frank K. schrieb:

Dein jetziger Weg ist jedenfalls nicht im Sinne der Erfinder.

Vielen Dank, Frank! Ja wohl war mir auch nie dabei, habe das aber fast 1:1 so im Internet gefunden. Ich werde für den Win-Branch

Frank K. schrieb:

  1. cfgmgr32
  2. setupapi

https://learn.microsoft.com/en-us/windows-hardware/drivers/install/enumerating-installed-device-interface-classes

studieren und dann implementieren!

Gruss Chregu

#7528017
Lesenswert?

Christian M. schrieb:

Es kommen ausschliesslich FT232 zu Einsatz.

Wenn man das auch in Zukunft wörtlich nimmt und keinen extra Treiber installiert, gibt es nur /dev/ttyUSBx; auch mit den 2-fach und 4-fach Konvertern von FTDI.

Die Idee mit dmesg finde ich nicht schön, das ist ein Ringpuffer. Wenn auf einem Rechner viel los ist, fehlen die ältesten Einträge irgendwann. Konverter, die schon beim Einschalten eingesteckt waren, findet man dann nicht. Das sicherste ist, in /dev nach ttyUSBx zu suchen. Man könnte auch ein paar andere Verzeichnisse nehmen, aber die sind teils bewegliche Ziele oder nicht immer vorhanden oder nicht aktuell. /dev wird es noch lange geben und es wird direkt vom Kernel selbst in Echtzeit aktualisiert.

/dev/ttySx sind die klassischen COM-Ports aus DOS-Zeiten. 2 bis 4 davon tauchen auch bei modernen Boards auf und auch, wenn es die Stecker garnicht gibt. Die würde ich direkt ignorieren.

#7528139
Lesenswert?

Hmmm schrieb:

Nicht für FTDI-Chips,

Niemand hindert dich daran, per einfacher Udev-Rule alle FTDI-Ports (oder auch nur die mit deiner Seriennummer etc.) als z.B. /dev/ttyFTDI0…99 oder /dev/dasGerät anzulegen.

Ansonsten erleuchtet vielleicht ein Blick in die Datei "/usr/lib/udev/rules.d/60-serial.rules" aus dem default udev/systemd-Regelwerk:

unter /dev/serial/by-id finden sich die USB-Seriel-Ports mit sehr aussagekräftigen Dateinamen. Da die Richtigen für deine Hardware rauszufiltern ist einfach.

#7528269
Lesenswert?

Mit libudev müsste das unter linux eigentlich machbar sein. Da kann man auch neue / entfernte Geräte detektieren. Auf die Schnelle hab ich das hier gefunden: http://gavv.net/articles/libudev-usb/ https://github.com/gavv/snippets/blob/master/udev/udev_monitor_usb.c https://github.com/gavv/snippets/blob/master/udev/udev_list_usb_storage.c

Muss man halt anpassen, so dass man nach TTYs statt USB Sachen sucht. Aber das Prinzip dürfte das selbe sein.

#7528544
Lesenswert?

Εrnst B. schrieb:

Niemand hindert dich daran, per einfacher Udev-Rule alle FTDI-Ports (oder auch nur die mit deiner Seriennummer etc.) als z.B. /dev/ttyFTDI0…99 oder /dev/dasGerät anzulegen.

Klar, wenn es praktikabel ist, im System herumzuwursteln. Hier scheint es ja um ein Produkt zu gehen, das auf fremden Systemen laufen soll.

Ohne Eingriffe heissen normalerweise nur CDC/ACM-Devices /dev/ttyACM*, die mit herstellerspezifischen Treibern /dev/ttyUSB*, daher mein Einwand bzgl. der FTDI-Chips.

900ss D. schrieb:

Es gibt für Linux ein Terminalprogramm "tio" für die Comsole. Dort gibt es die Möglichkeit, die zur Verfügung stehenden Ports zu listen. tio -L

Das grast unter Linux /dev/serial/by-id/* ab, unter den meisten anderen Systemen geht es in Richtung /dev/tty*.

tty.c, list_serial_devices(), die OS-spezifischen #defines findet man ab Zeile 61.

#7528690
Lesenswert?

Frank K. schrieb:

Die Win32 API stellt zwei Wege für das Enumerieren von (beliebigen) Devices zur Verfügung:

  1. cfgmgr32
  2. setupapi

Ich muss ja nicht mehr enumerieren, sondern will einfach eine Liste. Ist die Funktion CM_Get_Device_Interface_ListW was für mich? Gefunden in

Frank K. schrieb:

https://learn.microsoft.com/en-us/windows-hardware/drivers/install/enumerating-installed-device-interface-classes

Jetzt bin ich aber bei

1
chgport /query >tty.txt

gelandet. Das funktioniert. Dann kann ich dieselbe Routine nach dem Branch nehmen wie

Christian M. schrieb:

https://www.purebasic.fr/english/viewtopic.php?p=294057&sid=22f3d47c83264c33754c2ecfb54255f4#p294057

Gruss Chregu

#7540115
Lesenswert?

Christian M. schrieb:

1
chgport /query >tty.txt

geht hier auf einem Win7-Rechner. Aber jetzt fangen weitere Probleme an: Auf einem Win10-Rechner hat es den Befehl nicht gefunden. Gegoogelt: Aha, der heisst jetzt neu

1
change port /query

Funktioniert auch auf Win7, aber auf dem Win10-Rechner ums verrecken nicht => wieder nicht gefunden. Da dreht man doch durch!

Gruss Chregu

(Firma: Golden Delicious Computers) #7541064
Lesenswert?

Stefan F. schrieb:

Christian M. schrieb:

Kann ich bei Linux davon ausgehen, dass die Enumeration immer "/dev/ttyUSBx" ist?

Nein. /dev/ttyACMx ist auch gängig. Bei MacOS heißen sie wiederum anders. Letztendlich kann da jeder Treiber auch noch sein eigenes Süppchen kochen. Ein Programm dass sich auf ein bestimmtes Muster beschränkt, wird früher oder später Probleme bereiten. Ich würde zusätzlich zur Drop-Down Liste auch noch eine freie Texteingabe zulassen.

Hier ein Beispiel:

iMac $ ls -l /dev/cu.* crw-rw-rw- 1 root wheel 0x16000007 22 Nov 06:58 /dev/cu.Bluetooth-Incoming-Port crw-rw-rw- 1 root wheel 0x16000001 22 Nov 06:58 /dev/cu.usbserial-143340 crw-rw-rw- 1 root wheel 0x16000005 22 Nov 06:58 /dev/cu.usbserial-FTGHPOLK crw-rw-rw- 1 root wheel 0x16000003 22 Nov 06:58 /dev/cu.usbserial-FTH9L0T7 iMac $

Das sind 2 FTDI, ein Prolific 2303 und ein Bluetooth-Device (das aber nicht aktiv ist). Andere Devices melden sich manchmal mit -000000...

D.h. man könnte auf "/dev/ci.usbserial-FT*" filtern wenn man nur die FTDIs haben will.

Persönliche Seite #7541091
Lesenswert?

Mit C# / UWP ist es überraschend einfach:

1
using Windows.Devices.Enumeration;
2
using Windows.Devices.SerialCommunication;
3

4
DeviceInformationCollection serialDeviceInfos = await DeviceInformation.FindAllAsync(SerialDevice.GetDeviceSelectorFromUsbVidPid(0x0483, 0x374B));
5
foreach (var port in serialDeviceInfos)
6
{
7
  Debug.WriteLine(port.Id);
8
}

Wenn man keinen ollen Serial-Port sondern native USB-Geräte verwenden würde, könnte man unter allen Plattformen mit libusb sehr einfach alle angeschlossenen Geräte finden...

Persönliche Seite #7541122
Lesenswert?

Bauform B. schrieb:

So ein FTDI-Konverter ist doch ein "native USB-Gerät"?

Nach der Logik sind alle USB-Geräte "nativ". Ich meinte damit ein eigenes anwendungsspezifisches USB-Protokoll zu definieren, statt einen Serial-Port zu emulieren. Problem Nr. 1 ist ja offensichtlich dass aus PC-Sicht alle USB-Serial-Ports gleich sind und dem FTDI nicht anzusehen ist was für ein serielles Protokoll da jetzt drüber gehen soll; bei spezifischen USB-Protokollen kann man das Gerät eindeutig identifizieren.

#7541195
Lesenswert?

Christian M. schrieb:

Ja ja, genau!

Dann ist Dein Windows 10 merkwürdig. Alle Windows-Versionen, die ich hier im Büro habe (beginnend bei 2012 Server bis hin zum 2022 Server sowie Windows 10 und Windows 11) kennen "change".

Niklas G. schrieb:

Problem Nr. 1 ist ja offensichtlich dass aus PC-Sicht alle USB-Serial-Ports gleich sind

Sind sie das? Jeder wird mit dem dafür vorgesehenen Treiber angesprochen, FTDI mit FTDI-Treibern, WCH mit WCH-Treibern, Prolific mit Prolific-Treibern. Der generischen CDC-Treiber kommt nur selten zum Einsatz, denn die meisten USB-Seriell-Bridges sind keine CDC-Geräte.

Persönliche Seite #7541199
Lesenswert?

Harald K. schrieb:

Jeder wird mit dem dafür vorgesehenen Treiber angesprochen, FTDI mit FTDI-Treibern, WCH mit WCH-Treibern, Prolific mit Prolific-Treibern.

Aber alle erzeugen einen generischen COMx-Port. Aus Anwendungssicht gibt es da keinen Unterschied außer eben mit speziellen APIs. Aber auch daran kann man nur den Hersteller des Adapters identifizieren, nicht das tatsächliche Gerät. Zu wissen ob es ein Prolific, FTDI, ... -IC ist hilft kaum dabei zu entscheiden ob da ein Arduino, STK500, Analogmodem oder GPS-Tracker dran hängt.

Wobei: Eigentlich ist die CDC-ACM-Klasse nur dafür gemacht, Analog-Modems mit AT-Command-Set anzusteuern, somit sollten alle CDC-ACM-COM-Ports solchen Modems entsprechen. "Zufällig" erlauben alle(?) Betriebssysteme die Verwendung von solchen Ports auch mit anderen Geräten und anderen Anwendungen als Dial-Up. Daher werden wohl so gut wie alle der CDC-ACM-COM-Ports nicht für Analog-Modems genutzt.

#7541278
Lesenswert?

Niklas G. schrieb:

Aber auch daran kann man nur den Hersteller des Adapters identifizieren, nicht das tatsächliche Gerät. Zu wissen ob es ein Prolific, FTDI, ... -IC ist hilft kaum dabei zu entscheiden ob da ein Arduino, STK500, Analogmodem oder GPS-Tracker dran hängt.

Und? Das war zu den Zeiten, wo man "echte" serielle Schnittstellen verwendete, keinen Deut anders.

Bei USB-Seriell-Bridges ist mehr möglich, da kann dem Ding nämlich über den USB-Descriptor ein Name mitgegeben werden, der sagt, worum es sich handelt -- und dann hat anständig geschriebene Software die Möglichkeit, sich den rauszusuchen und anzuzeigen. Die kann dann natürlich nicht nur die Schnittstellen-API von 1993 verwenden.

Persönliche Seite #7541325
Lesenswert?

Harald K. schrieb:

Und? Das war zu den Zeiten, wo man "echte" serielle Schnittstellen verwendete, keinen Deut anders.

Richtig, aber das macht's nicht besser.

Harald K. schrieb:

Bei USB-Seriell-Bridges ist mehr möglich, da kann dem Ding nämlich über den USB-Descriptor ein Name mitgegeben werden, der sagt, worum es sich handelt

Ja, aber wenn man sich schon auf einen ganz spezifischen USB-Serial-IC einschränkt und diesen auch manuell konfiguriert/programmiert, kann man auch gleich sein eigenes Protokoll definieren. Das hat dann auch noch weitere Vorteile, wie eben mehrere Endpoints, automatische Flusskontrolle und Paketierung usw. Das erspart dann auch wieder einiges an Arbeit welche man beim Serialport hat.

Das o.g. C# Snippet habe ich genutzt um eine serielle Kommunikation mit einem eigenen Board zu implementieren. Dank dem gezeigten API war es schön einfach den Port zu finden. Beim Implementieren vom seriellen Protokoll mit Paketierung hab ich aber geflucht und mich gefragt warum ich nicht ein eigenes USB-Protokoll implementiert hab - hätte im Endeffekt wahrscheinlich einiges an Nerven gespart...

#7541375
Lesenswert?

Niklas G. schrieb:

Ja, aber wenn man sich schon auf einen ganz spezifischen USB-Serial-IC einschränkt und diesen auch manuell konfiguriert/programmiert, kann man auch gleich sein eigenes Protokoll definieren.

Wenn man Lust dazu hat, notfalls einen eigenen Devicetreiber zu klöppeln - und dem Anwender fest vorzuschreiben, mit welcher Software er mit seinen Geräten reden darf, dann kann man das so machen.

Persönliche Seite #7541378
Lesenswert?

Harald K. schrieb:

Wenn man Lust dazu hat, notfalls einen eigenen Devicetreiber zu klöppeln

Unnötig, libusb, WinUSB existiert.

Harald K. schrieb:

und dem Anwender fest vorzuschreiben, mit welcher Software er mit seinen Geräten reden darf

Der TO entwickelt doch eh seine eigene Software. Ob der Anwender jetzt die serielle Kommunikation oder die USB-Kommunikation sniffen muss um seine eigene Software zu implementieren tut sich nicht viel. Man kann beide Protokolle dokumentieren falls man Eigenentwicklungen erlauben möchte.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren