Datenverlust bei Empfang über COM-Port

  • VB.NET

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Datenverlust bei Empfang über COM-Port

    Hallo,
    bei meinem Testprogramm beobachte ich einen Datenverlust, bzw. Falschdarstellung beim Empfang von Daten über den Com-Port.

    Zur Situation: Ein externes Programm gibt über den Com-Port Werte für eine einzustellende Frequenz heraus. Jede Zahle / Zeile wird mit LF abgeschlossen.
    Verwendet werden zwei USB-Seriell-Wandler die überkreuz geschaltet sind, so dass ich die Daten wieder empfangen kann.
    Mit einem Terminal-Programm können alle Zahlen fehlerfrei empfangen werden in der Form:
    1000000
    2000000
    3000000
    10000000
    Ich möchte mir nun ein Programm schreiben, welches die Frequenzangaben auswertet und dann meinen Frequenzgenerator ansteuert.

    Wenn ich mir nun ein eigenes Programm schreibe, welches die Zahlen lesen soll, werden die Werte manchmal falsch gelesen und fehlerhaft ein Zeilenumbruch eingefügt, bzw. eine Zahl zerteilt.
    Einen Reim kann ich mir nicht daraus machen. Vielleicht könnt ihr mir weiterhelfen.
    Ist immer ein CR und LF notwendig oder reicht ein LF?

    Quellcode

    1. Public Class Form1
    2. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    3. SerialPort_20.Open()
    4. End Sub
    5. Private Sub SerialPort_20_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort_20.DataReceived
    6. Dim txt = SerialPort_20.ReadExisting
    7. Me.Invoke(Sub() Label3.Text = txt)
    8. Me.Invoke(Sub() ListBox2.Items.Add(txt))
    9. 'entnommen aus: https://www.vb-paradise.de/index.php/Thread/111205-Probleme-beim-auslesen-und-verwerten-von-werten-Com-Port-Ardurino-Mega/?postID=964894#post964894
    10. End Sub


    *Topic verschoben*
    Bilder
    • Zeilenumbruch.png

      1,96 kB, 251×241, 108 mal angesehen
    • Programmeinstellung.png

      52,99 kB, 1.083×1.104, 94 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    @egon Sag dem Port bei der Initialisierung, welche(s) Zeichen die Kennung für das Zeilenende ist/sind.
    Dann machst Du nichtt
    SerialPort_20.ReadExistingsondern SerialPort_20.ReadLine.
    Mach nicht 2 Invokes sondern ein BeginInvoke:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. SerialPort1.NewLine = Environment.NewLine ' Dein Zeichen halt
    3. SerialPort1.Open()
    4. End Sub
    5. Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
    6. Dim txt = SerialPort1.ReadLine
    7. Me.BeginInvoke(Sub()
    8. Label1.Text = txt
    9. ListBox1.Items.Add(txt)
    10. End Sub)
    11. End Sub

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Super! Danke.

    Mein Code sieht jetzt so aus:

    Quellcode

    1. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    2. SerialPort_20.NewLine = vbLf
    3. SerialPort_20.Open()
    4. End Sub
    5. Private Sub SerialPort_20_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort_20.DataReceived
    6. Dim txt = SerialPort_20.ReadLine
    7. Me.BeginInvoke(Sub()
    8. Label3.Text = txt
    9. ListBox2.Items.Add(txt)
    10. End Sub)
    11. End Sub


    Ich muss gestehen, dass ich das mit dem Invoke einfach aus einem deiner früheren Beiträge kopiert habe ohne es zu verstehen. Kannst du mir das bitte erklären.

    Dann muss ich meinen Code noch gegen Fehlbedieung schützen und prüfen ob der Com-Port überhaupt existiert und ob er schon geöffnet worden ist. Wie macht man das am geschicktesten? Mit Try Catch?

    egon schrieb:

    das mit dem Invoke
    Ein NET-Programm kann in mehreren Threads arbeiten. Wenn z.B. Berechnungen auf mehrere Threads verteilt werden, wird der Algorithmus schneller, sofern jeder Thread einen eigenen CPU-Kern zur Verfügung hat.
    Außerdem wird während der Zeit der Berechnung im anderen Thread die GUI nicht "eingefroren".
    Wenn GUI-Elemente verändert werden sollen, muss dies im Main-Thread der GUI stattfinden. Stell Dir vor, zwei Threads greifen gleichzeitig auf ein GUI-Element zu und der eine Thread sagt .ForeColor = Color.Green und der andere Thread sagt .ForeColor = Color.Blue. Damit so was nicht passiert, darf auf GUI-Elemente nur aus einem Thread heraus zugegriffen werden, dies passiert mit Invoke() und BeginInvoke().
    Invoke() wird unmittelbar ausgeführt, BeginInvoke() wird erst ausgeführt, wenn der GUI-Thread Zeit dafür hat (asynchron).
    Das SerialPort arbeitet in einem anderen Thread, also muss, wenn Du die Empfangsdaten an der GUI anzeigen willst, in den GUI-Thread invoked werden.
    Du hast das getan, indem Du jedes einzelne Control invoked hast, ich hab das ganze zusammengefasst zu einer "anonymen Methode" und dort alles reingepackt, was invoked werden muss.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    egon schrieb:

    einen eleganteren Weg gibt

    Na, bevor du eine Aktion ausführst, solltest du Prüfen ob alle Zustände, Daten und so vorhanden und "korrekt" sind. z.B. COM-Port existiert, ist offen usw.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen

    egon schrieb:

    mit Try Catch gegen Fehlbedieung schützt
    vielfältig.
    Ein serial Port kann eigentlich nicht falsch bedienen, man kann die Bedienung nur falsch programmieren.
    Die vorhandenen Ports rufst Du ab mit IO.Ports.SerialPort.GetPortNames, die packst Du in eine ComboBox, der User wählt sich eins aus.
    Dann musst Du den richtigen Code senden.
    Gibt es das Port nicht, kommt bei .Open() eine IO-Exception.
    Ist es das falsche Port, kommt eine blöde Antwort oder keine Antwort oder eine Timeout-Exception, das musst Du ausprobieren, das lässt sich von ferne nicht sagen.
    Hier mal ein Beispiel für ein nicht vorhandenes Port:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. SerialPort1.PortName = "COM1"
    3. Try
    4. SerialPort1.Open()
    5. Catch ex As IO.IOException
    6. MessageBox.Show("Port gibt es nicht")
    7. Return
    8. End Try
    9. SerialPort1.Write("bla")
    10. MessageBox.Show("da")
    11. End Sub
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!