PowerShell Serial
Auch wenn die wenigsten Computer heute noch eine parallele oder serielle Schnittstelle haben, so sind zumindest die seriellen Ports im IoT immer noch Standard. Über entsprechende USB-Adapter werden Arduino, ESP826 und Co programmiert und Ausgaben der kleinen Computer zu Diagnosezwecken ausgegeben. Aber auch per PowerShell lassen sich diese Schnittstellen sehr einfach verwenden
Serielle Ports finden
Zuerst sollten wie mal schauen, welche COM-Ports es gibt. Das erledigt ein Einzeiler in PowerShelll:
PS C:\>[System.IO.Ports.SerialPort]::GetPortNames() COM4
Mein PC halt also zumindest einen COM4-Port.
- Powershell – Show all available Serial
COM ports
https://www.mathewjbray.com/uncategorized/powershell-show-available-com-ports/
SerialPort instanzieren und parametrisieren
Über das gleiche Objekt kann ich einen Port instanzieren und die Parameter setzen.
$serial = New-Object System.IO.Ports.SerialPort $serial.PortName = "COM4" $serial.BaudRate = "9600" $serial.Parity = "None" $serial.DataBits = 8 $serial.StopBits = 1 $serial.ReadTimeout = 1000 # 1 sek
Wer schon mit seriellen Schnittstellen gearbeitet hat, sollte die Parameter zur Baudrate, Start/Stop-Bits etc. kennen. Es gibt auch eine Kurzform dafür.
$serial= new-Object System.IO.Ports.SerialPort COM4,9600,None,8,1
Das Objekt hat natürlich noch weitere Parameter:
PS C:\> $serial BaseStream : BaudRate : 9600 BreakState : BytesToWrite : BytesToRead : CDHolding : CtsHolding : DataBits : 8 DiscardNull : False DsrHolding : DtrEnable : False Encoding : System.Text.ASCIIEncoding+ASCIIEncodingSealed Handshake : None IsOpen : False NewLine : Parity : None ParityReplace : 63 PortName : COM4 ReadBufferSize : 4096 ReadTimeout : -1 ReceivedBytesThreshold : 1 RtsEnable : False StopBits : One WriteBufferSize : 2048 WriteTimeout : -1 Site : Container :
Und auch Event und Methoden sind vorhanden.
PS C:\> $serial | gm TypeName: System.IO.Ports.SerialPort Name MemberType Definition ---- ---------- ---------- DataReceived Event System.IO.Ports.SerialDataReceivedEventHandler DataReceived(System.Object, System… Disposed Event System.EventHandler Disposed(System.Object, System.EventArgs) ErrorReceived Event System.IO.Ports.SerialErrorReceivedEventHandler ErrorReceived(System.Object, Syst… PinChanged Event System.IO.Ports.SerialPinChangedEventHandler PinChanged(System.Object, System.IO.… Close Method void Close() DiscardInBuffer Method void DiscardInBuffer() DiscardOutBuffer Method void DiscardOutBuffer() Dispose Method void Dispose(), void IDisposable.Dispose() Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetLifetimeService Method System.Object GetLifetimeService() GetType Method type GetType() InitializeLifetimeService Method System.Object InitializeLifetimeService() Open Method void Open() Read Method int Read(byte[] buffer, int offset, int count), int Read(char[] buffer, int offse… ReadByte Method int ReadByte() ReadChar Method int ReadChar() ReadExisting Method string ReadExisting() ReadLine Method string ReadLine() ReadTo Method string ReadTo(string value) ToString Method string ToString() Write Method void Write(string text), void Write(char[] buffer, int offset, int count), void W… WriteLine Method void WriteLine(string text)
- SerialPort Klasse
https://docs.microsoft.com/de-de/dotnet/api/system.io.ports.serialport
Lesen und Schreiben
Schon bei den Methoden haben Sie sicher gesehen, dass es Write/Writeline und Read/Readline gibt. Zuerst müssen wie den Port aber öffnen und am Ende sollten wir ihn auch wieder schließen.
$serial.Open() $serial.Readtimeout = 1000 # 1 Sek. default ist unlimited $serial.WriteLine("123456789abcdef123456789abcdef123456789abcdef123456789abcdef") $serial.ReadLine() 123456789abcdef123456789abcdef123456789abcdef123456789abcdef
Bei Open habe ich bislang zwei Fehler provozieren können. Der letzte Fehler passiert beim Read
Fehler | Ursache |
---|---|
Exception calling "Open" with "0" argument(s): "Access to the port 'COM4' is denied." |
Der Port ist vermutlich durch eine andere Anwendung schon geöffnet und damit blockiert |
Exception calling "Open" with "0" argument(s): "The port 'COM2' does not exist." |
Prüfen Sie noch mal, ob es den angegeben Port in ihrem System gibt. |
Exception calling "ReadLine" with "0" argument(s): "The operation has timed out." |
Sie haben mit einer der "Read"-Methode auf ein Zeichen gewartet und es wurde während des ReadTimeout" kein Zeichen empfangen.. |
Wenn ich Bytes lese und keine Daten kommen, dann terminiert das Skript nach dem eingestellten "ReadTimeout" in Millisekunden mit einem Fehler
Über Puffer muss ich mir nur bedingt Gedanken machen, denn die meisten seriellen UARTs haben ein paar Bytes Buffer, um kurze Verzögerungen beim Einlesen zu erlauben. Aber Windows selbst hat natürlich auch einen eigenen FIFO-Buffer, der bei einfachen Operationen das Programmieren einfacher macht.
PS C:\> $serial | fl *buffer* ReadBufferSize : 4096 WriteBufferSize : 2048
Dennoch sollte das Skript nicht allzu lange irgendwo warten, denn irgendwann sind auch die 4096 Bytes aufgebraucht und die früher empfangenen Informationen werden überschrieben. Beim Schreiben können Sie natürlich auch "zu schnell" schreiben. Zwei Eigenschaften des Serial-Objects helfen ihnen dabei, die wartenden Bytes zu zählen.
PS C:\> $serial| fl bytes* BytesToWrite : 0 BytesToRead : 3135
Insofern könnten Sie den Fehler beim "Read" auch einfach vermeiden, wenn Sie das Feld "BytesToRead" abfragen. Das hilft aber nicht bei "ReadLine", welches auf ein Stringendezeichen (CHR(10) wartet. Wenn ein String im Buffer ist aber das Endezeichen fehlt, dann bricht "ReadLine" ab aber lässt die Zeichen im Buffer.
Steuerleitungen
Mit der Übertragung von Informationen über TxD und RxD ist es alleine ja nicht getan. Die serielle Schnittstelle kennt auch mit RTS/CTS einen Handshake und auch DSR/DTR/RI sind beim Modembetrieb interessante Aus- und Eingänge, die auch zweckentfremdet werden können. Alle Leitungen sind auch im Serial-Objekt enthalten. Der Breakstate ist aber keine eigene Leitung, sondern zeigt an, wenn RxD permanent als "0" steht und damit eine Unterbrechung signalisiert wird.
BreakState : False CDHolding : False (lesen) CtsHolding : False (Lesen) DsrHolding : False (Lesen) DtrEnable : False (Lesen und Setzen) Handshake : None RtsEnable : False (Lesen und Setzen)
Die Leitungen können also recht einfach gesetzt werden
$serial.DtrEnable = "true"
Leider haben aber die meisten USB-Seriell -Adapter im IoT-Bereich die Steuerleitungen nicht nach außen geführt und daher nicht nutzbar. Die RTS/CTS-Steuerung wird auch durch das Property "Handshake" bestimmt, welcher normalerweise auf "None" steht und einen von vier Werten annehmen kann
Bezeichnung | Wert | Bedeutung |
---|---|---|
None |
0 |
Kein Handshake. Die Leitungen sind ungenutzt |
XOnXOff |
1 |
Über TxD/RxD wird ein XON/XOFF genutzt, um die Übertragung zu steuern |
RequestToSend |
2 |
Die Steuerleitungen RTS/CTS werden genutzt |
RequestToSendXOnXOff |
3 |
XON/XOFF und RTS/CTS werden genutzt. |
- SerialPort Klasse
https://docs.microsoft.com/de-de/dotnet/api/system.io.ports.serialport - Software flow control
https://en.wikipedia.org/wiki/Software_flow_control
Beispiel mit D0
Auf der Seite SML Smartmeter Code habe ich mit einem Infrarot-Transistor die D0-Schnittstelle eines smarten Stromzählers ausgelesen. Da habe ich auch erst einmal mit einem einfachen USB/Seriell-Adapter und dem Notebook geprüft, was ankommt und wie leserlich das ist.
Für erste Gehversuche ist auch ein solcher USB/Seriell-Adapter nützlich, bei dem Sie einfach RxD und TxD verbunden haben.
So können Sie erste Informationen seriell senden und gleich wieder empfangen.
Interessant ist so ein Adapter natürlich auch mit einem NodeMCU, WeMos D1 oder anderen ESP32 oder ESP8266-Board, welches über die serielle Schnittstelle nicht nur per Strom versorgt wird, sondern z.B. auch WLAN-Scanning und Bluetooth-Scanning betreiben kann. So können Sie auf einem leistungsfähigen Notebook dann diese Tochtersysteme für spezielle Aufgaben einsetzen und die Ergebnisse direkt weiter verarbeiten.
Weitere Links
- SerialPort Klasse
https://docs.microsoft.com/de-de/dotnet/api/system.io.ports.serialport - Writing and Reading info from Serial
Ports
https://devblogs.microsoft.com/powershell/writing-and-reading-info-from-serial-ports/ - Powershell serial communication with
Arduino
https://forum.arduino.cc/index.php?topic=39804.0