Generelles Vorgehen beim Verarbeiten von zusammengehörigen Daten von der seriellen Schnittstelle

  • VB6

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

    Generelles Vorgehen beim Verarbeiten von zusammengehörigen Daten von der seriellen Schnittstelle

    Diese Frage ist eher allgemeiner Natur. Konkret geht es um das Digital-Multimeter VA18B unter Visual Basic 6.0, aber allgemeine Antworten sind auch willkommen.

    Folgendes Problem besteht:

    Das Gerät, also in meinem Fall das Multimeter, schickt alle paar Millisekunden Daten (Bytes). 14 Bytes ergeben ein vollständiges Paket. Aus diesen 14 Bytes lese ich u. a. die aktuell gemessene Spannung aus.

    Ich verwende in VB6 das MSComm-Steuerelement. Im OnComm()-Event frage ich die Daten ab:

    Visual Basic-Quellcode

    1. ' Ereignisse
    2. Case comEvReceive ' Anzahl empfangener Zeichen gleich RThreshold
    3. inp = comMultimeter.Input
    4. u = GetUBound(comMMInp)
    5. For I = 0 To GetUBound(inp)
    6. ReDim Preserve comMMInp(u + I + 1)
    7. comMMInp(u + I + 1) = inp(I)
    8. Next I
    9. If GetUBound(comMMInp) = 13 Then
    10. RecValueMultimeter comMMInp
    11. Erase comMMInp
    12. End If

    "RThreshold" steht auf "1", d. h. sobald ein Byte angekommen ist, wird das Event ausgelöst. GetUBound liefert lediglich den UBound eines Arrays (aber ohne Fehler, wenn der Array keinen Inhalt hat), comMMInp ist eine globale Variable (Byte-Array), RecValueMultimeter verarbeitet die 14 Bytes und zeigt den Wert an (die Routine ist hier irrelevant).

    Nun hat das bisher auch immer gut geklappt. Neuerdings passiert in meinem Programm aber etwas mehr und dadurch entstehen leichte Delays, die dazu führen, dass plötzlich nicht 1 Byte ankommt, sondern mehrere. Zufällig ergibt das auch meistens irgendwann 14 Bytes (UBound=13). Nur manchmal, wenn eine etwas größere Pause im Programm entsteht, sind im Puffer evtl. 100 Bytes.

    Zwischenzeitlich hatte ich den Puffer auf 14 Bytes gesetzt, aber das ergibt das selbe Problem.
    Anschließend hatte ich einfach immer die ersten 14 Bytes genommen und den Rest verworfen. Aber hier liegt das Problem. Unter Umständen habe ich den Anfang eines Pakets damit verworfen.

    Als Lösung hatte ich folgendes getestet:

    Visual Basic-Quellcode

    1. If GetUBound(comMMInp) >= 13 Then
    2. Dim value() As Byte
    3. value = comMMInp
    4. Erase comMMInp
    5. If GetUBound(value) > 13 Then
    6. For I = 14 To GetUBound(value)
    7. ReDim Preserve comMMInp(GetUBound(comMMInp) + 1)
    8. comMMInp(GetUBound(comMMInp)) = value(I)
    9. Next I
    10. End If
    11. ReDim Preserve value(13)
    12. RecValueMultimeter value
    13. End If


    Brachte leider auch nichts. Im Moment ist es wie folgt:

    Visual Basic-Quellcode

    1. ' Ereignisse
    2. Case comEvReceive ' Anzahl empfangener Zeichen gleich RThreshold
    3. inp = comMultimeter.Input
    4. u = GetUBound(comMMInp)
    5. For I = 0 To GetUBound(inp)
    6. ReDim Preserve comMMInp(u + I + 1)
    7. comMMInp(u + I + 1) = inp(I)
    8. If GetUBound(comMMInp) = 13 Then
    9. RecValueMultimeter comMMInp
    10. Erase comMMInp
    11. u = -1
    12. End If
    13. Next I


    Aber auch das führt zu zerstückelten Paketen.

    Die große Frage ist: Mache ich das generell richtig? Wie sorgt man dafür, dass kein Paket zerstückelt wird? Man kann (soweit ich weiß) im Falle des Multimeters nicht erkennen, wo ein Paket anfängt oder aufhört. Somit müsste ich quasi jedes ankommende Byte verarbeiten. Ich könnte also niemals was verwerfen. Zwar verwerfe ich meiner Ansicht nach derzeit nichts (es sei denn, mein Code ist falsch), aber es geht ja trotzdem nicht.

    Die Frage nochmal in Kurzform:

    Wie sorgt ihr dafür, dass Daten, die über die serielle Schnittstelle kommen, niemals zerstückelt werden, sofern immer mehrere Zeichen/Bytes einen Gesamtwert ergeben?
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum
    Es ist jetzt zwar auch schon länger her, dass ich Multimeter o.ä. ausgelesen habe...
    Normalerweise war es aber immer so, dass ein Carriage Return bzw. Line Feed ankam.
    Meinetwegen auch so diverse Sonderzeichen, die angeben, dass ein Paket fertig ist.
    Manchmal war auch direkt die feste Anzahl an Bytes angegeben. Ganz gut, wenn man
    dann noch eine Fehlerauswerteroutine einbaut, die testet, ob die Daten wirklich plausibel sind.

    Ich habe dann im Empfangsereignis die Bytes zusammengesetzt, so lange bis eines dieser Zeichen kam.
    Der Puffer muss halt groß genug sein, dass dein restliches Programm (und was auch immer noch so
    da herum rödelt) Zeit für andere Verarbeitungen hat. Erst wenn dann das Paket fertig war, habe ich
    es zur Weiterverarbeitung frei gegeben. Somit kommt es vielleicht zerstückelt an, wird dann aber
    ja zusammen gesetzt und als Ganzes weiter gereicht.

    Ich habe immer nur Strings aneinander gefügt, das geht bestimmt schneller als ein Redim in VB6.
    Ich lese mit dem selben Programm gleichzeitig noch ein Lichtmessgerät aus, das sendet Strings und am Ende ein CR. Da ist das kein Problem. Aber beim Multimeter habe ich Bytes, die alle zum Zusammensetzen der Daten nötig sind, also kein Trennzeichen enthalten. Hier steht was dazu: alexkaltsas.wordpress.com/2013…ta-from-va18b-multimeter/
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum
    ( Du willst aber doch nicht die 7-Segment-Anzeige auslesen, oder? Das fände ich wirklich etwas nervig )

    Am besten du probierst mal so einen seriellen Port Logger.
    Da kannst du genau verfolgen welche Daten kommen und gehen, samt den Steuerleitungen.
    Dann siehst du auch, wo es vielleicht mal hakt.
    Leider habe ich den Namen des Programms vergessen, müsste ich Morgen nach schauen.
    Ich verstehe deinen Code nicht ganz (keine Erfahrung mit VB6), aber kannst du nicht am Anfang des Events abfragen, ob mind. 14 Bytes im Puffer sind? Wenn nicht, verlässt du das Event und kommst beim nächsten Event wieder. Wenn ja, liest du die 14 Bytes aus und fragst die Restlänge ab. Wenn kleiner 14, gehst du wieder, wenn größer/gleich, liest du aus usw. Musst dann ein 2D-Array anlegen, z.B. 14x10 oder so, mehr als 140 Bytes wirst du auf einmal ja nicht haben, wo du dann deine Pakete auf einmal hinschiebst, wenn mehr als 28 Bytes im Puffer im Event.
    Und beim Verarbeiten gehst du in der Schleife deine Pakete durch.

    Habe ich dich jetzt richtig verstanden?
    @Lightsource Doch, genau das.

    @sonne75 Genau das mache ich ja jetzt, nur manchmal werden Daten "verschluckt" und dann habe ich unvollständige 14-Byte-Blöcke (die haben zwar 14 Bytes, aber die falschen).

    Evtl. gibt es doch was, woran man ein neues Paket erkennen kann. Ich habe das hier gefunden: gomatlab.de/datenuebertragung-va-18b-multimeter-t873.html

    Dort in Post 12 steht evtl. was. Ich melde mich, wenn ich da durchgestiegen bin. Danke erstmal.
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum
    Wie man erkennen kann ist die Zahl in den ersten 4 Bits die Durchnummerierung der Bytes. So kann man auch die empfangenen Bytes syncronisieren.


    Dann kannst du ja jetzt anhand der höherwertigen 4 Bits sehen, welche Datenbytes zusammengehören und die dazugehörigen niederwertige 4 Bits an die richtige Stelle des Arrays schieben (höherwertige 4 Bits als Index, nur halt 1 abziehen).
    Ich hab's nun tatsächlich hinbekommen. Da die Bytes entgegen meiner ursprünglichen Äußerung mit 1 bis 14 durchnummeriert sind, konnte ich meine Funktion wie folgt erweitern:

    Visual Basic-Quellcode

    1. ' Ereignisse
    2. Case comEvReceive ' Anzahl empfangener Zeichen gleich RThreshold
    3. Dim u2 As Integer
    4. inp = comMultimeter.Input
    5. u2 = GetUBound(inp)
    6. u = GetUBound(comMMInp)
    7. For i = 0 To u2
    8. ReDim Preserve comMMInp(u + i + 1)
    9. comMMInp(u + i + 1) = inp(i)
    10. Next i
    11. u = GetUBound(comMMInp)
    12. If u >= 13 Then
    13. Dim value() As Byte
    14. Dim found As Boolean
    15. Dim merkeI As Integer
    16. found = False
    17. merkeI = -1
    18. u2 = -1
    19. For i = 0 To u
    20. If Mid$(Hex$(comMMInp(i)), 1, 1) = "1" Then
    21. found = True
    22. End If
    23. If found Then
    24. u2 = GetUBound(value) + 1
    25. ReDim Preserve value(u2)
    26. value(u2) = comMMInp(i)
    27. If u2 = 13 Then
    28. merkeI = i + 1
    29. Exit For
    30. End If
    31. End If
    32. Next i
    33. If u2 = 13 Then
    34. Erase comMMInp
    35. RecValueMultimeter value
    36. End If
    37. End If

    Die wirklich interessante Stelle ist nun If Mid$(Hex$(comMMInp(i)), 1, 1) = "1" Then. Nun lese ich so lange ein, bis einmal das Paket komplett ist, den Rest verwerfe ich. Erst habe ich das mit dem Verwerfen nicht gemacht. Ging zwar auch, aber dann staute sich hin und wieder was an. Wenn ich das Programm beispielsweise 5 Sekunden pausiert habe, so musste es danach alle Daten dieser 5 Sekunden noch abarbeiten. Dadurch hatte ich quasi den Multimeter-Wert nicht mehr in Echtzeit.

    Danke für eure Tipps!

    Allerdings sind weitere Infos zur korrekten Vorgehensweise in solchen Fällen (nicht speziell auf mein Multimeter bezogen) immer noch willkommen!
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum

    Marcus Gräfe schrieb:

    Die wirklich interessante Stelle ist nun ...
    Wenn Du weißt, dass das Binärdaten sind, würde ich die zuerst in einen numerischen Typ wandeln, und da kannst Du mit AND, OR, AND NOT usw. wesentlich effizienter die Bits abfragen als per String-Verarbeitung.
    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!

    Marcus Gräfe schrieb:

    zu sehen
    Marcus ...

    Visual Basic-Quellcode

    1. Dim Wert = 123 ' Dein Wert halt
    2. Dim Bit0 = Wert And 1
    3. Dim Bit2 = Wert And 2
    4. Dim Bit3 = Wert And 4
    5. ' ...

    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!

    Marcus Gräfe schrieb:

    Vereinfachung
    String-Operationen dauern außerdem etwas länger als Bit-Befehle.
    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!