Hilfe mit Serialport.Basestream

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von FormFollowsFunction.

    Hilfe mit Serialport.Basestream

    Moin moin :)

    Ich bräuchte mal fachmännischen Rat.

    Problem:
    Ich nutze das normale Data_Received Event der seriellen Schnittstelle.
    Nun ist es so, dass bei sehr großen/schnellen Datenmengen (max. 512 bytes) im 1ms Bereich das Event einfach zu langsam ist und Verzögerungen von 2-3ms reinhaut.
    Baudrate ist 115200, das sollte eigentlich locker ausreichen.
    Zudem sind die Daten nur selten mal sehr groß.
    Die meiste Zeit sind es ca. 20 Bytes, die in sehr schnellem Takt (~1ms) übertragen werden.
    Aber selbst bei höherer Baudrate ist die Verzögerung da und zieht sich über die Dauer wie ein Gummiband (Nachladen).

    Mein derzeitger Code sieht ungefähr so aus:


    VB.NET-Quellcode

    1. Friend Sub SerialPort_Terminal_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort_Terminal.DataReceived
    2. Try
    3. Dim Daten As String = Nothing
    4. While SerialPort_Terminal.BytesToRead > 0
    5. If Port_offen = False Then
    6. If SerialPort_Terminal.IsOpen Then
    7. SerialPort_Terminal.Close()
    8. End If
    9. Else
    10. Daten = SerialPort_Terminal.ReadLine
    11. Me.BeginInvoke(Sub() Uebertragen(Zeitstempel() & Daten))
    12. End if
    13. End While
    14. Catch ex As InvalidOperationException
    15. If Port_offen = True Then
    16. MessageBox.Show(ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error)
    17. End If
    18. Me.Invoke(Sub() ComPort_verbinden(Button1, ComboBox1, ComboBox2, Button2, False, SerialPort_Terminal)) 'Verbindung trennen
    19. Catch ex As IO.IOException
    20. Uebertragen(ex.Message.ToString & Environment.NewLine)
    21. Catch ex As TimeoutException
    22. Uebertragen("Fehler Timeout > Baudrate oder gesendeten Befehl überprüfen" & Environment.NewLine)
    23. Catch ex As Exception
    24. MessageBox.Show(ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error)
    25. End Try
    26. End Sub


    Und ja, ich weiß, dass Readline als schlechter angesehen wird, als wenn ich einzelne Chars auslese aber es ändert nichts an der Timing Situation, das Problem besteht auch, wenn ich einzelne Chars auslese und zusammenfüge. :)



    Nach einiger Recherche hab ich gelesen, dass die BaseStream Methode die bessere Wahl ist, da das Data_Received Event bei hoher bzw. sehr schneller Datenübertragung zu langsam ist.


    Leider finde ich bzgl. VB.NET keine brauchbaren Informationen, wie ich das angehe.
    Ich finde nur einige kleine Beispiele mit C++ aber zum konvertieren reichen meine Kentnisse nicht aus.

    Zum Beispiel hier oder hier.

    Und mit Delegates bin ich auch noch ein Anfänger. :/


    Es geht mir nur um das Event, der Rest ist kein Problem.
    Soweit ich mal gelesen habe, muss dazu ein Task erstellt werden, damit das Event feuert bzw. anfängt den Port abzuhören?



    Wäre super, wenn mir da jemand helfen könnte.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „IfYesThanNo“ ()

    Also durch das Event DataReceived dauert es wirklich länger, daher nutze ich einen Thread mit abbrechbarer Endlosschleife. Dazu habe ich eine Queue(of Byte()), dort packe ich rein was zu senden ist. in der Schleife schaue ich ob was in der Schlange zum senden ist und sende bei Bedarf. Auch teste ich in der Schleife ob was zu lesen ist und kann Blitzschnell reagieren.

    Naifu schrieb:

    Setze die Eigenschaft ReceivedBytesThreshold auf, sagen wir mal 15.


    Funktioniert leider nicht.
    Selbst, wenn ich es auf 1 setze und nur 1 Zeichen im ~<=1ms Bereich empfangen will, habe ich Delays drin.
    Das Event ist einfach zu langsam, egal welche Einstellung man tätigt.

    Daher möchte ich es unbedingt mit BaseStream versuchen, alles andere wäre vergebens.
    Ich hatte noch eine alte exemplarische Mappe da, aber C#.
    Nutze Converter um das zu VB zu konvertieren:
    icsharpcode.github.io/CodeConverter/

    C#-Quellcode

    1. public partial class Form1 : Form
    2. {
    3. private Thread thread;
    4. private SerialPort serial;
    5. private bool exitFlag = false; // flag zum verlassen der endlos schleife
    6. private Queue<Byte[]> queue = new Queue<byte[]>();
    7. public Form1()
    8. {
    9. InitializeComponent();
    10. //zur schlange was zum senden hinzufügen, nur zur veranschaulichung
    11. queue.Enqueue(new byte[] { 0, 1, 2, 3, 4 });
    12. }
    13. private void IO_Thread()
    14. {
    15. Byte[] buffer;
    16. while(!exitFlag)
    17. {
    18. if(queue.Count > 0)
    19. {
    20. buffer = queue.Dequeue();
    21. serial.Write(buffer, 0, buffer.Length);
    22. }
    23. if(serial.BytesToRead > 0)
    24. {
    25. //lesen
    26. }
    27. }
    28. }
    29. //evtl. weitere optionen zur einstellung hinzufügen, also weitere Argumente
    30. private void StartListening(int baud, string portname)
    31. {
    32. //schauen ob bereits läuft
    33. if(thread != null && thread.IsAlive)
    34. {
    35. throw new Exception("Lief bereits :P");
    36. }
    37. serial = new SerialPort();
    38. serial.BaudRate = baud;
    39. serial.PortName = portname;
    40. try
    41. {
    42. serial.Open();
    43. }
    44. catch (IOException ex)
    45. {
    46. MessageBox.Show(ex.Message);
    47. return;
    48. }
    49. exitFlag = false;
    50. thread = new Thread(IO_Thread);
    51. thread.Start();
    52. }
    53. private void StopListening()
    54. {
    55. exitFlag = true;
    56. }
    57. }
    Wo liegt denn eigendlich genau dein Problem.
    Ich verstehe es wohl nicht wirklich.
    Also für was genau ist das zu langsam?
    Denn 115200 ist nicht schnell, und 512 Bytes ist nich viel.
    Willst du unter einer ms einen Response senden oder wie hat man das zu verstehen?
    Der Code im Event ist auch nicht gerade als Beschleuniger geeignet.
    Und Arduino geparrt mit Geschwindigkeit ist auch so ne Sache.

    Naifu schrieb:

    Wo liegt denn eigendlich genau dein Problem.
    Ich verstehe es wohl nicht wirklich.
    Also für was genau ist das zu langsam?
    Denn 115200 ist nicht schnell, und 512 Bytes ist nich viel.
    Willst du unter einer ms einen Response senden oder wie hat man das zu verstehen?
    Der Code im Event ist auch nicht gerade als Beschleuniger geeignet.
    Und Arduino geparrt mit Geschwindigkeit ist auch so ne Sache.



    Wie oben bereits erläutert, das Serialport Data Received Event ist zu langsam!
    Du kannst es gerne auch hier nachlesen, da ist das Problem recht gut erklärt.

    Das Terminal von der Arduino IDE schafft es, meins nicht.
    Die Daten kommen, sofern sie im wirklich kleinen Millisekundenbereich kommen, zu langsam an.
    Und es kommen mehrere Daten pro Millisekunde!
    Das ist bei Canbus Nachrichten eben üblich, da ist eine schnelle Verarbeitung Pflicht.

    Es sind mehrere Millisekunden zwischen einzelnen Daten, der Rest staut sich hinten dran und je länger das ganze geht, desto höher wird der Delay und es zieht sich wie ein Gummiband.
    Irgendwann macht dann nichtmal mehr die Anwendung mit und hängt sich auf, obwohl es schon im separaten Thread läuft

    BaseStream soll Asynchron laufen und ist deshalb auch die bessere Alternative bei schnellen Daten.

    Und dieser Code lässt sich mit keinem von mir bekannten Konverter einfach in vb.net konvertieren. ^^

    C-Quellcode

    1. byte[] buffer = new byte[blockLimit];
    2. Action kickoffRead = null;
    3. kickoffRead = delegate {
    4. port.BaseStream.BeginRead(buffer, 0, buffer.Length, delegate (IAsyncResult ar) {
    5. try {
    6. int actualLength = port.BaseStream.EndRead(ar);
    7. byte[] received = new byte[actualLength];
    8. Buffer.BlockCopy(buffer, 0, received, 0, actualLength);
    9. raiseAppSerialDataEvent(received);
    10. }
    11. catch (IOException exc) {
    12. handleAppSerialError(exc);
    13. }
    14. kickoffRead();
    15. }, null);
    16. };
    17. kickoffRead();


    Im Endeffekt würde ich diesen Code gerne in vb.net konvertiert haben, damit ich es ausprobieren kann.
    Mit Instant VB (Free Edition) konvertiert:

    VB.NET-Quellcode

    1. Dim buffer(blockLimit - 1) As Byte
    2. Dim kickoffRead As Action = Nothing
    3. kickoffRead = Sub()
    4. port.BaseStream.BeginRead(buffer, 0, buffer.Length, Sub(ar As IAsyncResult)
    5. Try
    6. Dim actualLength As Integer = port.BaseStream.EndRead(ar)
    7. Dim received(actualLength - 1) As Byte
    8. System.Buffer.BlockCopy(buffer,
    9. 0,
    10. received,
    11. 0,
    12. actualLength)
    13. raiseAppSerialDataEvent(received)
    14. Catch exc As IOException
    15. handleAppSerialError(exc)
    16. End Try
    17. kickoffRead()
    18. End Sub, Nothing)
    19. End Sub
    20. kickoffRead()
    Nicht ganz richtig konvertiert, wie bei den anderen Konvertern.

    Nach ein paar Anpassungen scheint es, mit einer Ausnahme, zu funktionieren.
    Ob das Timing nun damit besser ist, muss ich erst noch ausprobieren.

    VB.NET-Quellcode

    1. Dim Buffer(512 - 1) As Byte
    2. Private Sub Async_Read()
    3. If SerialPort_Terminal.IsOpen Then
    4. SerialPort_Terminal.BaseStream.BeginRead(buffer, 0, buffer.Length, Sub(ar As IAsyncResult)
    5. Try
    6. Dim Datenlaenge As Integer = SerialPort_Terminal.BaseStream.EndRead(ar)
    7. Dim Empfangen(Datenlaenge - 1) As Byte
    8. System.Buffer.BlockCopy(Buffer, 0, Empfangen, 0, Datenlaenge)
    9. Invoke(Sub()
    10. 'TextBox37.AppendText(Zeitstempel())
    11. Dim Zeile As String = Nothing
    12. For i = 0 To Datenlaenge - 1
    13. Zeile += Convert.ToChar(Buffer(i))
    14. Next
    15. TextBox37.AppendText(Zeile)
    16. End Sub)
    17. Catch ex As IOException
    18. TextBox37.AppendText(ex.Message.ToString & Environment.NewLine)
    19. End Try
    20. Async_Read()
    21. End Sub, Nothing)
    22. End If
    23. End Sub


    Das Einzige, was nicht funktioniert ist, dass das Event 2x gefeuert wird.
    Aus irgendeinem Grund wird, egal welche Datenlänge empfangen wird, das 1 Byte in einem Event gefeuert und dann der Rest in einem 2. Event.

    Ich erkenne es daran, dass der Zeitstempel 2x pro Zeile erscheint.
    Erster Zeitstempel mit 1 Byte, 2. Zeitstempel mit allen anderen bytes.

    Quellcode

    1. [11:45:06:255] 2[11:45:06:258] :61f:3d:1e:1a:4d:3b:39:1d:3e
    2. [11:45:06:267] 2[11:45:06:270] :617:12:12:3f:21:16:12:29:25
    3. [11:45:06:279] 2[11:45:06:281] :61e:37:5e:1b:18:19:27:1e:4f
    4. [11:45:06:291] 2[11:45:06:294] :61a:3f:30:2b:5b:12:1e:16:37
    5. [11:45:06:303] 2[11:45:06:307] :616:18:5b:5b:5d:34:56:19:60
    6. [11:45:06:315] 1[11:45:06:317] :620:17:18:13:1d:57:3c:26:20
    7. [11:45:06:327] 2[11:45:06:330] :61d:20:4c:1a:1b:23:52:1b:32
    8. [11:45:06:340] 1[11:45:06:342] :612:33:4e:29:54:25:10:5a:2b
    9. [11:45:06:352] 2[11:45:06:354] :621:29:15:32:12:57:59:2e:2c


    Aussehen sollte es eigentlich so:

    Quellcode

    1. [11:45:06:255] 2:61f:3d:1e:1a:4d:3b:39:1d:3e
    2. [11:45:06:267] 2:617:12:12:3f:21:16:12:29:25
    3. [11:45:06:279] 2:61e:37:5e:1b:18:19:27:1e:4f
    4. [11:45:06:291] 2:61a:3f:30:2b:5b:12:1e:16:37
    5. [11:45:06:303] 2:616:18:5b:5b:5d:34:56:19:60
    6. [11:45:06:315] 1:620:17:18:13:1d:57:3c:26:20
    7. [11:45:06:327] 2:61d:20:4c:1a:1b:23:52:1b:32
    8. [11:45:06:340] 1:612:33:4e:29:54:25:10:5a:2b
    9. [11:45:06:352] 2:621:29:15:32:12:57:59:2e:2c






    Edit:
    Bei einem Intervall von 1ms, kommt die Methode wohl auch nicht mehr hinterher.

    Quellcode

    1. 2:623:31:3a:47:5a:62:30:45:5d
    2. 1:627:5[11:58:04:269] b:50:35:17:4f:10:59:4f
    3. 1:616:3a[11:58:04:272] :23:4c:57:53:35:52:48
    4. 2:616:20:[11:58:04:275] 2f:4f:20:49:59:27:46
    5. 1:61e:32:3[11:58:04:278] a[11:58:04:281] :2c:5a:3a:11:54:14
    6. 2:623:46:12[11:58:04:284] :3e:12:27:19:41:4a
    7. 2:611:11:24:[11:58:04:287] 10:24:4c:17:3a:16
    8. 1:627:13:3d:4[11:58:04:290] 0:51:19:23:18:2e
    9. 2:611:13:2a:55[11:58:04:293] :59:1f:18:51:30
    10. 1:613:32:1b:55:[11:58:04:297] 3b:18:33:5e:62
    11. 2:61e:1b:2a:39:6[11:58:04:299] 2:57:3c:40:51
    12. 1:612:23:55:5e:11[11:58:04:302] :49:4e:29:31
    13. 1:613:5e:27:16:58:[11:58:04:306] 32:15:61:23
    14. 2:61c:14:26:3e:10:1[11:58:04:309] f:32:2c:14
    15. 2:612:24:1d:5c:4c:27[11:58:04:312] :4c:57:18





    Edit2:

    Seit .NET 4.5 soll es wohl auch "ReadAsync" geben, vielleicht ist das besser.
    Da muss ich mich dann auch mal einlesen. X/

    Vollzitat entfernt und starke Einrückung verringert. ~Thunderbolt

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „Thunderbolt“ ()

    Doch, der Konverter hat korreckt konvertiert !
    Das heist aber nicht zwangsläufig, daß es in der jeweils anderen Sprache, geanauso funktioniert, wie in der uhrsprünglichen.
    Nicht selten, muß man noch etwas anpassen.

    Und bitte nutze, beim Post erstellen die Forschaufunktion, die Code Formatierung ist ja total zerbröselt !
    Bei mir siehts so aus:


    Auf dem Bild von dir, ist ein Großteil des codes, viel zu weit eingerückt.


    Die Vorschau ist nur in der "Erweiterten Antwort" verfühgbar:


    @Marcus Gräfe
    Es wäre vieleicht gut, wenn die "Erweiterte Antwort" Standardeinstellung wäre.


    Und bitte die foreninterne Dateianhangfunktion, anstatt irgentwelcher Image-Hoster verwenden.
    [Forum] Anleitung Dateianhänge