Messschieber an RS232 bzw. USB zu RS232 Adapter auslesen -- Eingangsstring zerlegt sich

  • VB.NET

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von frave.

    Messschieber an RS232 bzw. USB zu RS232 Adapter auslesen -- Eingangsstring zerlegt sich

    Hallo Liebe Forumsgemeinde oder Forengemeinde?!

    ich hab folgendes Problem:
    Ich möchte über die Schnittstelle eines Mitutoyo Messschiebers dessen aktuellen Messwert auslesen. Der Messschieber sendet ein Byte (wenn ich mich nicht Irre) bei Tastendruck an die serielle Schnittstelle.
    Dafür nutze ich folgenden Codeansatz:

    VB.NET-Quellcode

    1. 'Serial Port Interfacing with VB.net 2010 Express Edition
    2. 'Copyright (C) 2010 Richard Myrick T. Arellaga
    3. '
    4. 'This program is free software: you can redistribute it and/or modify
    5. 'it under the terms of the GNU General Public License as published by
    6. 'the Free Software Foundation, either version 3 of the License, or
    7. '(at your option) any later version.
    8. '
    9. 'This program is distributed in the hope that it will be useful,
    10. 'but WITHOUT ANY WARRANTY; without even the implied warranty of
    11. 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12. 'GNU General Public License for more details.
    13. '
    14. ' You should have received a copy of the GNU General Public License
    15. ' along with this program. If not, see <http://www.gnu.org/licenses/&gt;.
    16. Option Strict Off
    17. Imports System
    18. Imports System.ComponentModel
    19. Imports System.Threading
    20. Imports System.IO.Ports
    21. Public Class frmMain
    22. Dim myPort As Array 'COM Ports detected on the system will be stored here
    23. Delegate Sub SetTextCallback(ByVal [text] As String) 'Added to prevent threading errors during receiveing of data
    24. Dim PufferString As String
    25. Private Delegate Sub DelegateSub() 'Private Datenanzeigen As New DelegateSub(AddressOf Anzeigen)
    26. Dim input As String
    27. Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    28. 'When our form loads, auto detect all serial ports in the system and populate the cmbPort Combo box.
    29. myPort = IO.Ports.SerialPort.GetPortNames() 'Get all com ports available
    30. cmbBaud.Items.Add(9600) 'Populate the cmbBaud Combo box to common baud rates used
    31. cmbBaud.Items.Add(19200)
    32. cmbBaud.Items.Add(38400)
    33. cmbBaud.Items.Add(57600)
    34. cmbBaud.Items.Add(115200)
    35. For i = 0 To UBound(myPort)
    36. cmbPort.Items.Add(myPort(i))
    37. Next
    38. cmbPort.Text = CStr(cmbPort.Items.Item(0)) 'Set cmbPort text to the first COM port detected
    39. cmbBaud.Text = CStr(cmbBaud.Items.Item(0)) 'Set cmbBaud text to the first Baud rate on the list
    40. btnDisconnect.Enabled = False 'Initially Disconnect Button is Disabled
    41. End Sub
    42. Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click
    43. SerialPort1.PortName = cmbPort.Text 'Set SerialPort1 to the selected COM port at startup
    44. SerialPort1.BaudRate = CInt(cmbBaud.Text) 'Set Baud rate to the selected value on
    45. 'Other Serial Port Property
    46. SerialPort1.Parity = IO.Ports.Parity.None
    47. SerialPort1.StopBits = IO.Ports.StopBits.One
    48. SerialPort1.DataBits = 8 'Open our serial port
    49. SerialPort1.Open()
    50. btnConnect.Enabled = False 'Disable Connect button
    51. btnDisconnect.Enabled = True 'and Enable Disconnect button
    52. End Sub
    53. Private Sub btnDisconnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDisconnect.Click
    54. SerialPort1.Close() 'Close our Serial Port
    55. btnConnect.Enabled = True
    56. btnDisconnect.Enabled = False
    57. End Sub
    58. Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
    59. 'MsgBox(SerialPort1.ReadExisting())
    60. ReceivedText(SerialPort1.ReadExisting()) 'Automatically called every time a data is received at the serialPort
    61. End Sub
    62. Private Sub ReceivedText(ByVal [text] As String)
    63. 'compares the ID of the creating Thread to the ID of the calling Thread
    64. If Me.rtbReceived.InvokeRequired Then
    65. Dim x As New SetTextCallback(AddressOf ReceivedText)
    66. Me.Invoke(x, New Object() {(text)})
    67. Else
    68. Me.rtbReceived.Text &= [text]
    69. DataGridView1.Rows.Add([text])
    70. End If
    71. End Sub
    72. Private Sub cmbPort_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmbPort.SelectedIndexChanged
    73. If SerialPort1.IsOpen = False Then
    74. SerialPort1.PortName = cmbPort.Text 'pop a message box to user if he is changing ports
    75. Else 'without disconnecting first.
    76. MsgBox("Valid only if port is Closed", vbCritical)
    77. End If
    78. End Sub
    79. Private Sub cmbBaud_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmbBaud.SelectedIndexChanged
    80. If SerialPort1.IsOpen = False Then
    81. SerialPort1.BaudRate = CInt(cmbBaud.Text) 'pop a message box to user if he is changing baud rate
    82. Else 'without disconnecting first.
    83. MsgBox("Valid only if port is Closed", vbCritical)
    84. End If
    85. End Sub
    86. Private Sub DataGridView1_CellContentClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
    87. End Sub
    88. End Class


    Das Funktioniert soweit auch hervorragend. Bei jedem Tastendruck wird eine neue Zeile in die Richtextbox geschrieben. Ich möchte allerdings am Ende die Daten in einem Datagridview haben. Das habe ich mit

    VB.NET-Quellcode

    1. DataGridView1.Rows.Add([text])
    versucht umzusetzen, allerdings verteilt sich dann der String manchmal nur auf eine neue Zelle, manchmal aber auch auf mehrere (siehe Beigefügtes Bild). Deweiteren möchte ich gerne dieses unnötige 01A an Anfang entfernt haben. Wenn ich das mit [text].remove(0,3) versuche, schneidet er aber auch relativ willkürlich.

    Für Hilfreiche Tipps wäre ich sehr Dankbar.
    Beste Grüße,
    Florian Rave
    Bilder
    • Form1.png

      39,78 kB, 928×424, 339 mal angesehen
    Willkommen im Forum. :thumbup:

    frave schrieb:

    wenn ich mich nicht Irre
    Lies bitte mal genau nach.
    Bytes oder String, wenn String, dann bitte noch das Encoding aufklären (ob "1,23" oder "1.23").
    Mach gleich Option Strict On, damit Du die Datentypen nicht durcheinanderwürfelst.
    Ich geh mal davon aus, dass dieser Messschieber nur mit einer Baudrate senden kann, also trag die fest in Dein Programm ein.
    Den Präfix entfernst Du so, und das Hinzufügen einer Zeile funktioniert auch:

    VB.NET-Quellcode

    1. Dim txt = "01A-123.456"
    2. txt = txt.Substring(3)
    3. DataGridView1.Rows.Add(txt)
    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!
    Hallo,

    vielen Dank schon mal für die Tipps. Werde ich morgen gleich ausprobieren...

    Also der Dezimaltrenner ist auf jeden fall der Punkt.

    ZumThema Byte, da habe ich wohl vertan. Die Sendung setzt sich aus ing. 12 Datenbyts zusammen. Wenn ich davon ausgehe, das 8 Bit (=1Zeichen) ein Byte sind?! Diese sind wie folgt zu verstehen:
    D1=Datenformat immer 0
    D2=Kanalnummer immer 1
    D3=ID immer A
    D4=Vorzeichen kann + oder - sein
    D5-D12= ist der Messwert also 10mm entsprechen 00010.00
    Abgeschlossen wird mit einem CR
    Die Gesamtsendung sieht dann so aus: 01A+00010.00CR

    Gruß
    Ganz langsam.
    Was ist ein Datenbyts? Es gibt Bits und Bytes, aber Byts hab ich noch nie gesehen.

    Nicht Begriffe durcheinanderwürfeln.
    So wie ich das verstehe, besteht ein Paket aus 13 Characters:

    Quellcode

    1. 1 Datenformat: Immer Ziffer "0"
    2. 2 Kanalnummer: Immer Ziffer "1"
    3. 3 ID: Immer großer, lateinischer Buchstabe "A"
    4. 4 Vorzeichen: Pluszeichen oder Minuszeichen bzw. Bindestrich
    5. 5 Teil des Messwerts: Ziffer "0" oder Ziffer "1" oder Ziffer "2" .... oder Ziffer "9"
    6. 6 Teil des Messwerts: Wie oben
    7. 7 Teil des Messwerts: ...
    8. 8 Teil des Messwerts
    9. 9 Teil des Messwerts
    10. 10 Dezimaltrennzeichen: Punkt
    11. 11 Teil des Messwerts
    12. 12 Teil des Messwerts
    13. 13 End-Zeichen: Zeilenumbruch (bzw. im Englischen "Carriage Return")
    Das sind Zeichen. Durch welche Bytes diese Zeichen dargestellt werden, hängt vom Encoding ab. Welches Encoding verwendet wird, musst Du wissen. Das steht normalerweise in der Dokumentation des Gerätes. Ich tippe mal auf ASCII, kann aber auch z.B. UTF-8 sein. Wahrscheinlich macht es keinen Unterschied, weil alle gängigen Encodings ASCII-kompatibel sind, und hier keine speziellen Zeichen verwendet werden.

    Abgeschlossen wird mit einem CR
    Das würde bedeutet, dass ein solches Paket 14 Zeichen lang ist:

    Quellcode

    1. 1 Datenformat
    2. ...
    3. 11 Messwert
    4. 12 Messwert
    5. 13 C
    6. 14 R
    Also Zeichen 13 wäre ein großer, lateinischer Buchstabe "C", und Zeichen 14 ein großer, lateinischer Buchstabe "R". Ich glaube nicht, dass das der Fall ist.
    Viel eher glaube ich, dass es ein Zeilenumbruch sein soll.

    Ich gehe an dieser Stelle mal von ASCII aus.
    Diese Werte:

    Quellcode

    1. Datenformat: 0
    2. Kanalnummer: 1
    3. ID: A
    4. Vorzeichen: Positiv
    5. Messwert: 12345.67
    würden diesen String ergeben: 01A+12345.67 Wobei der Zeilenumbruch am Ende ein nicht-druckbares Zeichen ist. Deshalb kann man ihn hier nicht sehen.
    In VB würde man diesen String so darstellen:

    VB.NET-Quellcode

    1. 'In VB gibt es keine Möglichkeit Steuerzeichen direkt in das String-Literal zu schreiben.
    2. Dim Packet As String = "01A+12345.67" & System.Text.Encoding.ASCII.GetString({13})
    In C# könnte man eine Escape-Sequenz verwenden:

    C#-Quellcode

    1. string A = "01A+12345.67\r";

    Wenn man sich die ASCII-encodierte Repräsentation ansieht, kommt dieses Byte-Array heraus: {48, 49, 65, 43, 49, 50, 51, 52, 53, 46, 54, 55, 13}
    Das Byte mit dem Wert 48 steht für die Ziffer "0", 49 für die Ziffer "1", 65 für den großen, lateinischen Buchstabe "A", 43 für das Pluszeichen, und so weiter.

    Hier musst Du noch etwas genauer erklären: Sind Datenformat und Kanalnummer wirklich Characters? Denn hier wird für Datenformat 0 ein Byte mit dem Wert 48 übertragen, und nicht ein Byte mit dem Wert 0! Was genau der Fall ist, konnte ich aus Deiner Angabe nicht herauslesen, und der Code sieht zu sehr nach Spaghetti aus, als dass man da was verstehen könnte.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Guten Abend,
    natürlich ist hier ein Byte gemeint gegewesen, ich bitte die Unterschlagung des "e" zu entschuldigen. Wenn ich das damals in der Schule richtig verstanden habe benötigt es 8Bits um ein Zeichen (=Character) darzustellen. Diese 8 Bits bilden dann aber auch gleichzeitig ein Byte. Richtig?

    Wenn jetzt also Statt ASCII bspw. UTF... Übertragen worden wäre müsste ja Datenmüll angekommen, was es aber ja offensichtlich nicht tut (Siehe Bild in meinem ersten Beitrag). Also ist diese Frage geklärt, es geht um ASCII.

    Weiter, mein von mir so achtlos benutztes "CR" repräsentiert natürlich den noch aus der Schreibmaschinenzeit stammenden Papiervorlauf (englisch: carriage) und Wagenrücklauf (english: return), was ja zusammen eben CR ergibt. Hatte das als bekannt vorausgesetzt. Sorry! Das tun Die von der Erstellung der Bedinungsanleitungen übrigens auch...

    So jetzt aber genug der Definitionen. Und ja es handelt sich um Spaghetticode, ich kanns halt nicht besser.

    Zu den Fakten, ich glaube diese Diskussion über Encoding können wir uns sparen, da ja in der RichText das Richtige ankommt, sogar der unsägliche "CR", in Form einer neuen Zeile. Der Messwert stimmt zu 100% mit dem auf dem Messschieber angezeigten Wert überein. Meine ursprüngliche Frage war ja wieso kommt die Sendung des Messschiebers manchmal im ganzen, in einer Zeile des DatagridViews an und manchmal nicht. Liegt es vielleicht an dem "CR"? Oder liegt es an dem Punkt? Also dem eventuellen falschen Dezimaltrennzeichen? Oder liegt es an einer falschen Behandlung des eingehenden Strings?
    Besten Dank.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „frave“ ()

    frave schrieb:

    Diese 8 Bits bilden dann aber auch gleichzeitig ein Byte. Richtig?
    Dies ist Elementar-Wissen, <CR> auch.
    Ich hab Dir mal ein Snippet gebastelt, ungetestet.

    VB.NET-Quellcode

    1. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. ' separate Methode
    3. Me.InitPort()
    4. End Sub
    5. Private Sub InitPort()
    6. ' Default-Einstellungen
    7. Me.SerialPort1.BaudRate = 9600
    8. Me.SerialPort1.DataBits = 8
    9. Me.SerialPort1.StopBits = System.IO.Ports.StopBits.One
    10. Me.SerialPort1.Parity = System.IO.Ports.Parity.None
    11. Me.SerialPort1.Handshake = System.IO.Ports.Handshake.None
    12. ' spezielle Einstellungen, musst Du ausprobieren
    13. Me.SerialPort1.Encoding = System.Text.Encoding.ASCII 'UTF8
    14. ' Zeilenende für Empfangsdaten
    15. Me.SerialPort1.NewLine = Convert.ToChar(13)
    16. End Sub
    17. Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
    18. ' Daten auslesen
    19. Dim txt = Me.SerialPort1.ReadExisting.Substring(3) ' 01A abschneiden
    20. ' Info in den Main-Thread übertragen
    21. Me.Invoke(Sub() Me.TextBox1.Text = txt)
    22. 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!
    Die ganze serielle Kommunikation (kurz SIO) erfolgt asynchron und - für heutige Verhältnisse - unglaublich langsam. SerialPort.DataReceived wird aber aufgerufen, sobald Daten zur Verfügung stehen. Ergo ergibt sich folgendes Bild:

    - Es plätschern Bits in den seriellen Port herein
    - Diese werden zu Bytes zusammengesetzt
    - Ein interner Timer löst dann - für heutige Rechengeschwindigkeiten - gelegentlich ein DataReceived aus
    - Du holst die Daten aus dem Empfangsbuffer
    - Und zeigst sie an

    Dadurch bekommst du gelegentlich auch fragmentierte Daten, da der Timer stumpf nach seinem Intervall das DataReceived-Event auslöst, ergo hast du manchmal halbe Zeilen in deinem RichEdit.

    Abhilfe: Lese Daten aus dem seriellen Port so lange aus (und speichere sie ggf. zwischen), bis du sicher sein kannst, das du ein komplettes Datenelement zusammen hast. Erst dann Anzeigen.

    OlafSt schrieb:

    gelegentlich ein DataReceived

    OlafSt schrieb:

    da der Timer stumpf nach seinem Intervall das DataReceived-Event auslöst

    @frave Gugst Du SerialPort.NewLine.
    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!
    Hallo,

    vielen Dank für die Anregungen!
    Habe jetzt den Fehler gefunden...
    Es liegt am ReadTimeout oder dem RecivedBytesThreshold.
    Den ReadTimeout habe ich auf 5(Millisekunden?) gestellt.
    Könnte aber auch am RecivedBytesThreshold liegen, diesen habe ich auf 13 gestellt und siehe da es Funktioniert!
    Bin mir nicht an welchem Parameter es jetzt lag, muss ich noch ausprobieren... Werde es aber melden.

    Gruß