Forum: PC-Programmierung SerialPort abfragen (VB2008)


von Micha (Gast)


Lesenswert?

Hallo,
ich verwende VB2008.net und möchte Daten vom SerialPort abfragen.
Leider hängt es und VB zeigt mir keinen Fehler an.
Die Komponente SerialPort habe ich auf der Oberfläche.

Kurzbeschreibung:
Wenn im SerialCom der Wert 1 vorhanden ist, soll Bild 1 auf der 
Oberfläche angezeigt werden, bei Wert 2 wieder ausgeblendet und Bild 2 
sichtbar werden. Der Wechsel findet alle ~5 - 10 Sekunden statt - im 
WindowsTerminal kann ich die Werte empfangen (Com4).

Würde mich freuen, wenn sich jemand mein Code ansehen könnte

schon mal Danke
Micha
1
Imports System.IO.Ports
2
Public Class Form1
3
    Dim Daten As Integer
4
5
    'Daten empfangen
6
    Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
7
        SerialPort1.PortName = "com4"
8
        SerialPort1.BaudRate = 9600
9
        SerialPort1.WriteTimeout = 1000
10
        SerialPort1.ReadTimeout = 1000
11
        SerialPort1.Open()
12
    End Sub
13
14
    Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
15
        While (Daten > 4)
16
            Daten = SerialPort1.ReadExisting
17
        End While
18
    End Sub
19
20
    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
21
        Do
22
            Select Case Daten
23
                Case "1"
24
                    PictureBox1.Visible = True
25
                    PictureBox2.Visible = False
26
                    PictureBox3.Visible = False
27
28
                Case "2"
29
                    PictureBox1.Visible = False
30
                    PictureBox2.Visible = True
31
                    PictureBox3.Visible = False
32
                Case "3"
33
                    PictureBox1.Visible = False
34
                    PictureBox2.Visible = False
35
                    PictureBox3.Visible = True
36
            End Select
37
        Loop Until Daten > 4
38
    End Sub
39
End Class

von STK500-Besitzer (Gast)


Lesenswert?

Lass dir doch mal die empfangenen Daten anzeigen.

von Lasse S. (cowz) Benutzerseite


Lesenswert?

1
Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
2
        While (Daten > 4)
3
            Daten = SerialPort1.ReadExisting
4
        End While
5
    End Sub

Vielleicht hängt's auch hier? Er sollte in dem EventHandler nur soviele 
Daten lesen, wie auch wirklich da sind. Und nicht einfach immer weiter, 
bis da zufällig mal was kleinergleich 4 rüber kommt.

Allgemein hast du da recht viele Schleifen drin, die schnell mal 
unendlich lang werkeln. Wenn das unbedingt sein muss (nein, muss es 
nie), dann bau da mal ein Application.DoEvents rein. Aber vermeide die 
Schleifen lieber gleich.

von Albrecht H. (alieninside)


Lesenswert?

Wie Lasse S. bereits schrieb, das mit dem

While (Daten > 4)

ist schlecht, "Sub SerialPort1_DataReceived" ist ja zumindest vom 
Verständnis her eine Art Interruptroutine, die gibts zwar unter VB wie 
Sand am Meer, aber in diesem Fall ist sie ja noch an eine echte externe 
Hardware, nämlich den seriellen Port gebunden und solche Routinen sollte 
man möglichst schnell abarbeiten und wieder verlassen, bevor sie erneut 
aufgerufen werden.
Aber genau das macht "While (Daten > 4)" nicht, (was passiert z.B. wenn 
gerade nur ein einziges Byte den "DataReceived"-Event ausgelöst hat?).

Du solltest die "SerialPort.BytesToRead"-Eigenschaft checken um 
festzustellen wieviele Bytes sich gerade im Empfangspuffer des seriellen 
Ports befinden wenn "DataReceived" ausgelöst wird.

Nachtrag: In der Regel wird "DataReceived" bereits nach einem 
empfangenen Byte ausgelöst, muss aber nicht so sein, je nachdem was das 
Betriebsystem gerade macht können es auch mal zwei, drei oder noch mehr 
Bytes sein.

von Micha (Gast)


Lesenswert?



Hab den Code nun etwas verändert und siehe da es funktioniert.

Ich hatte noch einen weiteren Fehler und zwar wurde der SerialPort nicht 
abgefragt.

Hier die abgeänderte Version:
1
Imports System.IO.Ports
2
Imports System.Windows.Forms
3
Imports System
4
5
Public Class Sim
6
7
    Dim Verbindung As Double
8
    Dim Daten As String
9
10
    Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
11
12
        SerialPort1.PortName = "com4"
13
        SerialPort1.BaudRate = 9600
14
        SerialPort1.WriteTimeout = 1000                        ' 1000 ms = 1 Sek.
15
        SerialPort1.ReadTimeout = 1000                         ' 1000 ms = 1 Sek.
16
        SerialPort1.Open()
17
        If SerialPort1.IsOpen Then
18
            Verbindung = 1
19
        End If
20
    End Sub
21
22
    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
23
        txtAusgabe.Text = Daten
24
        Do
25
            'If SerialPort1.BytesToRead = 0 Then
26
            Daten = SerialPort1.ReadExisting
27
            Application.DoEvents()
28
            'Else
29
            'End If
30
            Select Case Daten
31
                Case "1"
32
                    PictureBox1.Visible = True
33
                    PictureBox2.Visible = False
34
                    PictureBox3.Visible = False
35
36
                Case "2"
37
                    PictureBox1.Visible = False
38
                    PictureBox2.Visible = True
39
                    PictureBox3.Visible = False
40
                Case "3"
41
                    PictureBox1.Visible = False
42
                    PictureBox2.Visible = False
43
                    PictureBox3.Visible = True
44
            End Select
45
        Loop Until Verbindung = 0
46
    End Sub
47
48
49
    Private Sub Ampelsimulation_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
50
        Verbindung = 0
51
        SerialPort1.Close()
52
    End Sub
53
End Class

von Albrecht H. (alieninside)


Lesenswert?

Schön, dass es funktioniert. Übung macht den Meister. Nett gemeint! Aber 
das ist leider noch lange nicht!

Zumindest vom reinen Ansatz her sogar noch schlechter als dein erster 
Entwurf, weil du jetzt nur noch eine Schleife und keine Events mehr zum 
Auslesen des seriellen Ports verwendest. Dein Programm hängt praktisch 
in der "Loop Until Verbindung = 0"-Schleife fest und ohne 
"Application.DoEvents()" würde vermutlich gar nichts mehr gehen, ganz 
schlechter Stil!

Zudem ist gar nicht festgelegt was passieren soll, wenn 
"SerialPort1.ReadExisting" zufällig mal mehr als 1 Byte liefert.

Dann bau lieber wieder alles in das "SerialPort1_DataReceived"-Event 
ein. Im Prinzip ist es doch ganz einfach: "SerialPort1_DataReceived" 
wird automatisch aufgerufen wenn der serielle Port etwas empfangen hat, 
dann schaut man mit ....

Ok, ich gebs ja zu, es ist dann doch nicht ganz so einfach, wie es auf 
den ersten Blick in die VS2008 Dokumentation ausschaut. Ich hab das 
gestern einfach mal selber ausprobiert, das hat dann zwar auch gleich 
irgendwie so funktioniert, wie ich mir das vorgestellt habe, aber halt 
nur irgendwie. Unschöne Warnungen wegen "Threadübergreifender Zugriffe 
auf eine Form" und eine ganz üble Exception beim Beenden des Programms 
haut einem der VS2008 Compiler um die Ohren wenn man die Sache im 
VB6-Stil angeht. Microsoft hat da für den Gelegenheits-VB-Programmierer 
inzwischen doch ein paar Fußangeln ausgelegt.

Trotzdem, eines der wenigen schönen Beispiele für die Verwendung des 
seriellen Ports unter VB2005/2008, ist hier zu finden:
http://www.activevb.de/tipps/vbnettipps/tipp0071.html

Schön deshalb, weil es in sehr kompakter Form:

- zeigt wie man das serielle Port-Control dynamisch erzeugt

- zeigt wie man die verfügbaren COM-Ports auflistet

- zeigt wie man nicht nur das "DataReceived"-Event behandelt sondern 
auch alle anderen die mit dem COM-Port zu tun haben.

- auch die, inzwischen zum Leidwesen und Unverständnis vieler 
VB-Gelegenheitsprogrammierer, notwendige "Thread-sichere"-Programmierung 
am Beispiel einer Textausgabe, nicht unterschlägt

- scheinbar alles soweit richtig macht, dass es den Anwender auch nicht 
mit der, recht häufig diskutierten "Der E/A-Vorgang wurde wegen eines 
Threadendes oder einer Anwendungsanforderung abgebrochen."-Fehlermeldung 
bzw. einer "System.IO.IOException" nerven muss.

Nach Durchsicht dieses Beispiels kann man dann vermutlich auch den Kauf 
dieses Buches verzichten:
http://home.comcast.net/~hardandsoftware/books2.htm

Obwohl der Mann ja durchaus einiges von der Sache zu verstehen scheint, 
seine "DesktopSerialIO.dll" hat jedenfalls in einem ersten Test schon 
mal funktioniert:
http://home.comcast.net/~hardandsoftware/DesktopSerialIO.htm

von Micha (Gast)


Lesenswert?

Albrecht H. schrieb:
> Zudem ist gar nicht festgelegt was passieren soll, wenn
> "SerialPort1.ReadExisting" zufällig mal mehr als 1 Byte liefert.

Naja, zufällig wird ja nichts versendet, denn mehr versende ich mit dem 
Mikrocontroller nicht.

Mit dem anderen hast du schon Recht, jedoch kamen nur noch die Besagten 
Thread Fehler und ich musste es so lösen.
Deinen Tipp werde ich mir noch ansehen, leider hab ich nicht mehr viel 
Zeit am Code zu basteln

von Albrecht H. (alieninside)


Lesenswert?

Micha schrieb:
> Albrecht H. schrieb:
>> Zudem ist gar nicht festgelegt was passieren soll, wenn
>> "SerialPort1.ReadExisting" zufällig mal mehr als 1 Byte liefert.
>
> Naja, zufällig wird ja nichts versendet, denn mehr versende ich mit dem
> Mikrocontroller nicht.
>
> Mit dem anderen hast du schon Recht, jedoch kamen nur noch die Besagten
> Thread Fehler und ich musste es so lösen.

Verständlich. Aber wenns nur mal so zum testen oder schnell vorführen 
ist, lässt sich die Sache mit den "IllegalCrossThreadCalls" auch einfach 
abschalten, dazu einfach folgende Zeile unterhalb von "Private Sub 
Form1_Load ..." einfügen, dann verhält sich das Programm so als wäre es 
unter VB2003 kompiliert worden, da gabs das noch nicht:

Control.CheckForIllegalCrossThreadCalls = False

und im verlinkten Beispiel wird ja dann gezeigt wie es richtig geht.

> Deinen Tipp werde ich mir noch ansehen, leider hab ich nicht mehr viel
> Zeit am Code zu basteln

von Eugler (Gast)


Lesenswert?

Albrecht H. schrieb:
> lässt sich die Sache mit den "IllegalCrossThreadCalls" auch einfach
> abschalten

Naja, auch irgendwie dirty. Dann lieber einen delegaten anlegen, der den 
GUI Thread handelt.

von juppi (Gast)


Lesenswert?

Noch einfacher

Schicke vom Controller nur 1 Byte als ByteBefehl.

Diesen auswerten und deine Ampeln schalten.

P.S.  das DataReceived Ereignis kann eingestellt werden, nach wieviel 
Byte es Auslöst.

von Micha (Gast)


Lesenswert?

Ok, Danke euch

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.