donniduck
Goto Top

Powershell und Kommunikation (lesen und schreiben) mit RS-232, COM1, serielle Schnittstelle

Hallo,
über die Powershell soll ein externes Gerät mit serieller Schnittstelle(RS232/COM1) angesprochen und gesteuert werden. Das Senden der Daten an das Gerät stellt kein Problem dar.

Allerdings habe ich große Probleme die seriellen Daten korrekt zu empfangen, da Daten verloren gehen bzw. die Übertragung nicht stabil läuft!

Mein erster Ansatz für die Lösung dieser Aufgabe war folgender Beitrag auf der Microsoft-Webseite mit folgenden Quellcode:

#Daten schreiben 

$port= new-Object System.IO.Ports.SerialPort COM1,9600,None,8,one
$port.open()
$port.WriteLine("Hello world")  
$port.Close()

#Daten lesen

$port= new-Object System.IO.Ports.SerialPort COM1,9600,None,8,one
$port.Open()
$port.ReadLine()

Die Steuerung des Gerätes erfolgt nach folgenden Schema:
  1. Befehl senden
  2. Daten lesen/empfangen
  3. Befehl senden
  4. Daten lesen/empfangen
  5. usw.

Erster Beispielquellcode
#INIT => Allgemein
[string]$buffer = " ";  

# Befehlsyntax für RS-232-Gerät
[string]$rs232_event_check = "/event/log/entries ?`r";  
[string]$rs232_event_transfer = "/event/log/read ?`r";  

# INIT => Eigenschaften der seriellen schnittstelle
$port = New-Object System.IO.Ports.SerialPort;

[string]$port.PortName = "COM1";  
[int]$port.BaudRate = 19200;
[int]$port.DataBits = 8;
[string]$port.Parity = "None";  
[string]$port.StopBits = "One";  
[string]$port.Handshake = "XOnXOff";  

# START => Kommunikation

# RS-232-Kommunikation => START
$port.Open();
# RS-232-Kommunikation => Gerät ansprechen
$port.WriteLine($rs232_event_check);
# RS-232-Kommunikation => Gerätdaten abfragen
$buffer = $port.ReadLine();
# Testausgabe auf Konsole
echo $buffer;
$port.WriteLine($rs232_event_check);
$buffer = $port.ReadLine();
echo $buffer;

# RS-232-Kommunikation => ENDE
$port.Close()

Das angesprochene Gerät sendet nach dem Empfangen des Befehls sofort eine Antwort, je nach Abfragegeschwindigkeit werden nicht alle Daten übertragen bzw. gehen verloren!

Deshalb möchte ich das Empfangsregister (Puffer/Buffer) über eine Ereignisabfrage (Interrupt/Flag) abfragen. Nur wenn neue Daten anliegen erfolgt eine Abfrage der Daten und eine Auswertung auf das Abschlusszeichen. Allerdings ist hierzu die Dokumentation von Microsoft und im Internet bezogen auf die Powershell dürftig.

Für die Abfrage soll die Methode DataReiceved() genutzt werden, allerdings komme ich hier nicht weiter, da der Code von Microsoft nicht für die Powershell ist?! Wie kann ich diese Methode und das Abfragen der Events korrekt in die Powershell einbinden?

In folgenden Artikel wird auf diese Thematik eingegangen:
1. SerialPort and DataReceived Event
2. Top 5 SerialPort Tips

Content-Key: 203134

Url: https://administrator.de/contentid/203134

Ausgedruckt am: 28.03.2024 um 08:03 Uhr

Mitglied: Friemler
Friemler 11.03.2013 aktualisiert um 19:32:11 Uhr
Goto Top
Hallo DonniDuck,

schau mal hier und hier nach. Dort sind zwar nur Beispiele für Timer-Events, das sollte sich allerdings auf Deinen Fall übertragen lassen.

Evtl. so (ungetestet):
$rs232_event_check = "/event/log/entries ?`r";  
$rs232_event_transfer = "/event/log/read ?`r";  

$port = new-Object System.IO.Ports.SerialPort

$port.PortName = "COM1"  
$port.BaudRate = 19200
$port.DataBits = 8
$port.Parity = "None"  
$port.StopBits = "One"  
$port.Handshake = "XOnXOff"  

$null = Register-ObjectEvent $port DataReceived -SourceIdentifier "SerialPort.DataReceived" -Action {OnDataReceived}  

$port.Open()
$port.WriteLine($rs232_event_check)

Start-Sleep -Seconds 2

$port.Close()

Unregister-Event -SourceIdentifier "SerialPort.DataReceived"  



function OnDataReceived {
  $response = $port.ReadLine()
  echo $response
}

Gruß
Friemler
Mitglied: DonniDuck
DonniDuck 12.03.2013 um 11:32:27 Uhr
Goto Top
Hallo Friemler,

vielen Dank für den Denkanstoß, was mich auf jeden Fall schon ein gutes Stück weiter bringt, hier die aktuelle Version - leider ohne korrekte Funktion:

# INIT => Allgemein

[string]$buffer = " ";  
[string]$rs232_event_check = "/event/log/entries ?`r";  
[string]$rs232_event_transfer = "/event/log/read ?`r";  

# INIT => Eigenschaften der seriellen schnittstelle

$port = New-Object System.IO.Ports.SerialPort;

[string]$port.PortName = "COM1";  
[int]$port.BaudRate = 19200;
[int]$port.DataBits = 8;
[string]$port.Parity = "None";  
[string]$port.StopBits = "One";  
[string]$port.Handshake = "XOnXOff";  

# INIT => Funktionen

function GET_RS232-DATA {
 $buffer = $port.ReadExisting();
 write_host "Interrupt";  
 write_host $buffer;
}

# INIT => Interrupts

Register-ObjectEvent -InputObject $port -EventName DataReceived -Action {GET_RS232-DATA} -SourceIdentifier Status_RS232; 

##########################################################
# START
##########################################################

$port.Open();
$port.WriteLine($rs232_event_check);
Start-Sleep -Seconds 10;
$port.Close();
Unregister-Event -SourceIdentifier Status_RS232;

Der Interrupt bzw. das Ereignis wird vermutlich korrekt eingerichtet, allerdings springt er nicht in die Funktion und gibt die Testausgabe auf der Konsole aus!

Beim Starten des Programmes wird folgende Ausgabe auf die Konsole geschrieben:

Id              Name            State      HasMoreData     Location             Command                  
--              ----            -----      -----------     --------             -------                  
2               Status_RS232    NotStarted False                                GET_RS232-DATA

Ein Anhaltspunkt dafür, dass die Ereignisabfrage korrekt eingebunden ist, allerdings wird in der Spalte "State" das Problem liegen! Es fehlt mir vermutlich noch ein Parameter bzw. Schalter zum automatischen initialisieren der Ereignisabfrage?

Microsoft schreibt dazu:
Use the DataReceived event on the port object with the Register-ObjectEvent cmdlet.



Was ja im Quellcode in Zeile 28 schon vorgenommen wurde!

Der Befehl "$port | Get-Member -MemberType Event" bringt folgende Anzeige auf die Konsole:

TypeName: System.IO.Ports.SerialPort

Name		MemberType	Definition                                                                                                                
----		----------	----------                                                                                                                
DataReceived	Event		System.IO.Ports.SerialDataReceivedEventHandler DataReceived(System.Object, System.IO.Ports.SerialDataReceivedEventArgs)
Disposed	Event		System.EventHandler Disposed(System.Object, System.EventArgs)                                                             
ErrorReceived	Event		System.IO.Ports.SerialErrorReceivedEventHandler ErrorReceived(System.Object, System.IO.Ports.SerialErrorReceivedEventArgs)
PinChanged	Event		System.IO.Ports.SerialPinChangedEventHandler PinChanged(System.Object, System.IO.Ports.SerialPinChangedEventArgs)
Mitglied: Bitschubser
Bitschubser 24.01.2014 um 18:53:56 Uhr
Goto Top
Hi DonniDuck,

ein etwas später Kommentar, aber vielleicht hilft er noch, ich hatte mir hier auch ewig den Kopf zerbrochen. Der Event wird schon getriggert, das sieht man, wenn man in der Action einfach etwas auf den Host schreibt. Nur habe ich nie Daten vom seriellen Port gesehen, bis ich nach einigem Probieren auf den MessageData-Parameter gekommen bin:

$event = Register-ObjectEvent -InputObject $port -EventName DataReceived -MessageData $port -SourceIdentifier "SerialPort.DataReceived" -Action { $event.messagedata.readexisting() | Out-Host }

Und vor dem Schließen des Ports sollte dann noch ein Start-Sleep stattfinden, sonst ist der Kanal zu bevor das Gerät fertig geantwortet hat.

Viele Grüße vom Bitschubser