Forum: PC-Programmierung VB.net ließt vom Com-Port nur die ersten Bytes


von discard (Gast)


Lesenswert?

Hallo zusammen,

ich hab ein kleines Problem... ich lese von einem Com Port Daten ein die 
ich im Hex Format anzeigen will.

Diese Daten will ich nach einer bestimmten Zeichenfolge durchsuchen und 
einen Timer stoppen, wenn diese Zeichenfolge vom Com Port erkannt wird. 
Die Daten speichere ich in eine Stringvariable, damit ich mit 
".contains" nach der erwarteten Zeichenfolge suchen kann.

Mein Problem: Alle 2-3 Versuche wird diese Stringvariable nur mit den 
ersten empfangenen Zeichen gefüllt.

Empfängt der Com Port z.B.
1
FF 11 B5 3E 09
 wird meine Stringvariable endlos mit
1
FF 11 FF 11 FF 11
...... gefüllt, immer nur die ersten Zeichen. Ich habe !zeitgleich! mit 
VB den Eltima Serial Port Monitor laufen lassen und kann damit sicher 
sein dass die Daten korrekt empfangen werden und es kein Problem vom 
Adapter o.ä. ist.

Hier mein Code:

Receive-Event:
1
Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
2
     Dim RXByte As Byte
3
        Do
4
            RXCnt = 0
5
            Do
6
                If SerialPort1.IsOpen Then RXByte = SerialPort1.ReadByte
7
                RXArray(RXCnt) = LookUpTable(RXByte >> 4)
8
                RXCnt = RXCnt + 1
9
                RXArray(RXCnt) = LookUpTable(RXByte And 15)
10
                RXCnt = RXCnt + 1
11
                RXArray(RXCnt) = " "
12
                RXCnt = RXCnt + 1
13
                SpaceCount = (SpaceCount + 1) And 31
14
                If SpaceCount = 0 Then
15
                    RXArray(RXCnt) = Chr(13)
16
                    RXCnt = RXCnt + 1
17
                    RXArray(RXCnt) = Chr(10)
18
                    RXCnt = RXCnt + 1
19
                Else
20
                    If (SpaceCount And 3) = 0 Then
21
                        RXArray(RXCnt) = " "
22
                        RXCnt = RXCnt + 1
23
                        RXArray(RXCnt) = " "
24
                        RXCnt = RXCnt + 1
25
                    End If
26
                End If
27
            Loop Until (SerialPort1.BytesToRead = 0)
28
29
            Me.Invoke(New MethodInvoker(AddressOf Display))
30
        Loop Until (SerialPort1.BytesToRead = 0)
1
Private Sub Display()
2
        mydata &= RXArray
3
        RichTextBox1.Text &= mydata 'hier werden die Daten aus der Variable in einer Textbox angezeigt, damit ich sehe ob die Daten richtig ankommen.
1
Private Sub Timer13_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer13.Tick
2
'Timer überprüft ob die erwartete Zeichenfolge ankommt / im String ist.
3
        If mydata.Contains("11 25 31 25") Then Timer2.stop


Wie gesagt, der Fehler tritt nur alle paar Versuche auf. Manchmal gehts 
erst beim dritten Mal, manchmal gehts 10x ohne Probleme.

Hat jemand einen Tip was ich falsch mache?

von Christian E. (cerker)


Lesenswert?

Das sieht für mich nach dem typischen Anfänger(?)-Fehler aus, davon 
auszugehen, dass eine ganze Nachricht empfangen wird und dann das 
"DataReceived"-Event kommt.

So funktioniert serielle Kommunikation nicht. Sobald das erste Byte da 
ist, kommt das Event und du kannst solange lesen bis der Puffer 
leerläuft. Das ist aber nicht zwangsweise die ganze Nachricht oder auch 
nur deine Nachricht und nicht irgendwas anderes sein. Es ist ein mehr 
oder weniger zufälliges Stück aus dem Datenstrom, abhängig unter vielem 
anderen davon wann dein Prozess dran kam das Event abzuarbeiten.

Bei USB-Adaptern ist das ganze noch von der USB-Paketierung überlagert 
(deswegen funktioniert es überhaupt erst manchmal).

Das musst du mit einer eigenen Zustandsmaschine regeln. Wie man die 
ausführt, hängt vom Protokoll ab. Ob du CR/CRLF als Endezeichen nimmst 
und/oder ein Timeout und/oder dein Protokoll Start- und Endetags hat.

Außerdem musst du in .NET eine BIN-HEX Wandlung nicht wie Assembler 
programmieren. Dafür gibt es String.Format.

Gruß,
Christian

: Bearbeitet durch User
von Schlaumaier (Gast)


Lesenswert?

Vielleicht liegt es daran.

https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.readbyte?view=dotnet-plat-ext-7.0

Zitat:
Da die SerialPort Klassenpuffer daten und der Datenstrom, der in der 
BaseStream Eigenschaft enthalten ist, nicht enthalten ist, kann die 
beiden konflikten, wie viele Bytes zum Lesen verfügbar sind. Die 
BytesToRead Eigenschaft kann darauf hinweisen, dass Bytes gelesen 
werden, aber diese Bytes können möglicherweise nicht auf den datenstrom 
zugreifen, der in der BaseStream Eigenschaft enthalten ist, da sie an 
die SerialPort Klasse gepuffert wurden.


Ich persönlich bin aber mehr dafür die Daten einfach einzulesen und sie 
wenn fertig zu verarbeiten. Das hat besonders bei Serial ohne 
Rückmeldung den Vorteil das keine Daten verloren gehen. Ein Problem was 
es gibt, seit es Serielle Übertragung gibt.

von Christian E. (cerker)


Lesenswert?

Weia ist das gruselig autoübersetzt .. wenn man mal das Original liest, 
versteht man auch was Sache ist:

"Because the SerialPort class buffers data, and the stream contained in 
the BaseStream property does not, the two might conflict about how many 
bytes are available to read. The BytesToRead property can indicate that 
there are bytes to read, but these bytes might not be accessible to the 
stream contained in the BaseStream property because they have been 
buffered to the SerialPort class."

Das sagt nur, dass die SerialPort-Klasse selbst einen FIFO hat, der die 
Daten womöglich schon aus dem BaseStream geholt hat und man das daher 
nicht mehr selbst tun kann (wofür es in normalen Situationen auch keinen 
Grund gibt).

Ich hab nochmal nachgedacht, und der ganze Ansatz ist nicht besonders 
günstig. Ich würde das wie gesagt mit einer FSM machen. In C könnte ich 
dir die jetzt hinschreiben, aber VB kann nicht (besonders nicht um 
Mitternacht).

Kurz, du brauchst ein Array in dem dein Stop-Kommando als Konstante 
steht, und einen Index dazu. Dieser steht zuerst auf dem ersten Zeichen.

Für jedes Zeichen prüfst du, ob es diesem entspricht. Wenn ja, setzt du 
den Index auf das zweite und überprüfst für das nächste ankommende Byte 
ob es dem zweiten entspricht .. wenn ja, ab aufs dritte, wenn nicht, 
zurück auf Anfang. Kommst du am Ende vom Stop-Kommando an, hast du es 
erfolgreich empfangen.

Also sagen wir du willst einfach S,T,O,P als Zeichenkette haben:
Index auf S:
es kommt ein 'A' -> nix tun
es kommt ein 'C' -> nix tun
es kommt ein 'S' -> Index auf das T
es kommt ein 'O' -> zurück auf S
es kommt ein 'S' -> Index auf T
es kommt ein 'T' -> Index auf O
es kommt ein 'O' -> Index auf P
es kommt ein 'L' -> zurück auf S
es kommt ein 'I' -> nix tun
es kommt ein 'S' -> Index auf T
es kommt ein 'T' -> Index auf O
es kommt ein 'O' -> Index auf P
es kommt ein 'P' -> zurück auf S, und Aktion auslösen

Dabei aus der SerialPort immer nur ein Byte ziehen, das puffern 
übernimmt die schon für dich.

Die ganze BIN-HEX Umwandlung und String.Contains ist für das gegebene 
Problem völlig unnötig.

Gruß,
Christian

von Schlaumaier (Gast)


Lesenswert?

Christian E. schrieb:
> Weia ist das gruselig autoübersetzt .. wenn man mal das Original liest,
> versteht man auch was Sache ist:

hihi.

Ich habe den Text auch 5 x lesen müssen. Und DAS passiert sehr selten.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.