[VB.NET] Seriële communicatie probleem

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • nephilimcrt
  • Registratie: Juni 2011
  • Laatst online: 09-09 18:12

nephilimcrt

Mad Scientist

Topicstarter
Hey iedereen,

Ik zit al de hele avond enorm te prutsen om RS232 communicatie met m'n Arduino voor elkaar te krijgen. Gisteren heeft een GoT-ter me al geholpen met de Arduino code (die nu werkt voor zover ik kan checken).

Maar nu zit ik met de VB code die niet meewerkt. Het zou als volgt moeten verlopen:

- VB stuurt string via COM poort naar Arduino
- Arduino ontvangt deze en stuurt 'm net zo hard weer terug
- VB ontvangt string en zet deze in een textbox

Dit werkt een heel klein beetje, maar de string die VB terugkrijgt is meestal helemaal onzinnig. Maar heel soms komt ie wel goed over.

Om te beginnen maar eens de VB code:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
Imports System
Imports System.IO.Ports
Imports System.Threading

'This is the module that will handle communications with the Arduino UNO.


Public Class Arduino_Comms

    Dim arduinoport As New SerialPort()



    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'Hier alleen crap die geladen moet worden bij het laden van de Form.

    End Sub

    Private Sub Main()

    End Sub


    Private Sub btnSendToArduino_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSendToArduino.Click
        'When the 'Send To Arduino' button is pressed, the value in the textbox will be sent to the Arduino (duh).

        Dim TypedInText
        TypedInText = boxInput.Text

        sendToArduino(TypedInText)

        'boxInput.Clear()
    End Sub

    'This function sends its input to the Arduino via the serial port

    Private Sub sendToArduino(ByRef arduinoCommand)

        Dim commandToSend As String

        commandToSend = arduinoCommand
        'commandToSend = Str(arduinoCommand)

        arduinoport.PortName = "COM3"
        arduinoport.BaudRate = 9600

        Try
            arduinoport.Open()

            arduinoport.Write(commandToSend)

            arduinoport.Close()
        Catch ex As System.Exception
            MsgBox("Whoops. Cannot write serial data.")
            'Do whatever you want to do when there's an error here.
            'In most cases, the error is caused by the fact that the Arduino is not connected.
        End Try

        'This is just a test. Check immediately if there's anything to read
        receiveSerialData()

    End Sub

    Private Sub receiveSerialData()
        Dim nBytes As Integer
        Dim rxByte As Byte
        Dim rxChar As Char
        Dim rxString As String

        arduinoport.PortName = "COM3"
        arduinoport.BaudRate = 9600

        Try
            arduinoport.Open()
            nBytes = arduinoport.BytesToRead
            boxReceived.Text = ("Bytes to read: " + Str(nBytes))

            While (arduinoport.BytesToRead > 0)
                'rxByte = (arduinoport.ReadByte)
                rxString = arduinoport.ReadLine()
            End While

            arduinoport.Close()
        Catch ex As System.Exception
            MsgBox("Whoops. Cannot read serial data.")
            MsgBox(ex.Message)
            'Do whatever you want to do when there's an error here.
            'In most cases, the error is caused by the fact that the Arduino is not connected.
        End Try

        boxReceived.Text = rxString

    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        receiveSerialData()
    End Sub
End Class


Het hangt nog een beetje van testjes aan elkaar. Direct na het versturen van de string roep ik meteen 'receiveSerialData' aan, want dat is de enige manier waarop ie nog iets binnenkrijgt. Als ik het doe met de knop ('Button1_Click') gebeurt er helemaal niks. Waarschijnlijk omdat ik te traag ben.

Als ik de string ditiseenteststring~ verstuur (die tilde heb ik nodig als EOL karakter), dan moet ik 3 keer versturen voordat er ook iets terugkomt. Wat er terugkomt ziet er dan zo uit:

d?????????????????ditisee?W??????????ditiseenteststring~

Zoals je ziet is de laatste wel ok. Ik heb al geprobeerd langer te wachten, door een sleep van een x aantal milliseconden ertussen te zetten, maar dit levert ook weinig nuttigs op.

Hieronder de code op de Arduino, wie weet zit daar nog iets fout, maar uit mijn tests blijkt dat in ieder geval niet.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
* This sketch is a test for sending strings back and forth via the serial port.
*/

void setup() {
    // initialize the serial communication:
  Serial.begin(9600);

}

void loop() { 
 
   char rxbyte;
   String rxstring;
   
  
  while (true) 
  {   
    // check if data has been sent from the computer:
    if (Serial.available()!= 0) {
   
      // read the most recent byte:
      rxbyte = Serial.read();
      // add the most recent byte to the string:
      rxstring = rxstring + rxbyte;
      //send the received string back over the serial port:
      
      // check if end of string
      if (rxbyte == '~') {
        break; // break out of the while loop
      }
    }
  }
  
  if (rxstring != "")
  {
    Serial.println(rxstring);
    Serial.flush(); 
  }
  
  rxstring = "";
  
  // ready to receive a new string
   
}



Ik kan op internet maar bijzonder weinig goede voorbeelden vinden en wat ik vind werkt niet als ik het zelf probeer. Er moeten toch mensen zijn die vaker dit soort dingen vanuit VB.NET doen?

Alle hulp is welkom en mijn dank is al bij voorbaat eindeloos!

Tapping progress on the shoulder and saying: "More forwards, please!".


Acties:
  • 0 Henk 'm!

  • nephilimcrt
  • Registratie: Juni 2011
  • Laatst online: 09-09 18:12

nephilimcrt

Mad Scientist

Topicstarter
Update: Ik heb inmiddels een kant-en-klaar voorbeeld gevonden dat werkt met de code op mijn Arduino. Daar lag het dus inderdaad niet aan.

Ik weet nog steeds niet wat ik fout deed, maar ik ga dit voorbeeld wel omknutselen.

Voor de liefhebbers: http://tiktakx.wordpress....rfacing-with-vb-net-2010/

Tapping progress on the shoulder and saying: "More forwards, please!".


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 12:01
Je zit te frunnikken met de poort, laat dat ding toch open staan.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Armageddon_2k
  • Registratie: September 2002
  • Laatst online: 10-09 15:29

Armageddon_2k

Trotse eigenaar: Yamaha R6

VB.net heeft een standaard component voor Seriele data afhandeling. (deze kan je op je GUI slepen)
Ik zou zeggen: Open die poort als je je programma start, en sluit hem als je je programma sluit.

Werk daarnaast met de events die dit component heeft, dit werkt beter dan steeds zelf de data proberen uit te lezen.

furthermore vind ik dit:

arduinoport.BytesToRead > 0
rxString = arduinoport.ReadLine()

Niet echt een fraaie combo, je vraagt af of er nieuwe data is, en probeert daarna een complete string line uit te lezen.
Dit houdt zoveel in als: bij elke letter iemand op papier zet, probeer jij een zin te lezen.

[ Voor 38% gewijzigd door Armageddon_2k op 09-11-2011 08:57 ]


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 12:01
Armageddon_2k schreef op woensdag 09 november 2011 @ 08:51:
Werk daarnaast met de events die dit component heeft, dit werkt beter dan steeds zelf de data proberen uit te lezen.
Om de events die het ding heeft op een correcte manier af te handelen moet je meer doen alleen al omdat die op een andere thread worden afgevuurd. Simpelweg pollen werkt minstens zo goed en is een heel stuk simpeler om me te beginnen.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Armageddon_2k
  • Registratie: September 2002
  • Laatst online: 10-09 15:29

Armageddon_2k

Trotse eigenaar: Yamaha R6

farlane schreef op woensdag 09 november 2011 @ 09:04:
[...]
Om de events die het ding heeft op een correcte manier af te handelen moet je meer doen alleen al omdat die op een andere thread worden afgevuurd. Simpelweg pollen werkt minstens zo goed en is een heel stuk simpeler om me te beginnen.
Bijzonder verhaal, nog nooit last van gehad. De events die het component aftrapt kan je gewoon in je GUI gebruiken.

Acties:
  • 0 Henk 'm!

  • nephilimcrt
  • Registratie: Juni 2011
  • Laatst online: 09-09 18:12

nephilimcrt

Mad Scientist

Topicstarter
Dank voor het commentaar allemaal. Ik ben weliswaar een doorgewinterde IT-er, maar absoluut geen echte programmeur. Ik probeer met hulp van het internet en mensen zoals jullie mijn doel te bereiken. Inmiddels heb het voorbeeld dat ik eerder al noemde omgebouwd voor mijn doeleinden en dat werkt fantastisch.

Tapping progress on the shoulder and saying: "More forwards, please!".


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 12:01
Armageddon_2k schreef op woensdag 09 november 2011 @ 10:48:
Bijzonder verhaal, nog nooit last van gehad. De events die het component aftrapt kan je gewoon in je GUI gebruiken.
Owh?
The DataReceived event is raised on a secondary thread when data is received from the SerialPort object. Because this event is raised on a secondary thread, and not the main thread, attempting to modify some elements in the main thread, such as UI elements, could raise a threading exception.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • nephilimcrt
  • Registratie: Juni 2011
  • Laatst online: 09-09 18:12

nephilimcrt

Mad Scientist

Topicstarter
Edit: Ik ga hier toch nog even m'n 'uitdaging' neerzetten. Na een flink aantal mislukte pogingen moet ik concluderen dat ik niet de juiste aanpak gebruik.

Onderstaande code omvat het hele 'DataReceived' event. Als alle data ontvangen is, wordt de inhoud in een textbox (rtbReceived.Text) geplaatst. Dat werkt allemaal prima, maar is niet wat ik wil.

Ik wil dat de ontvangen data in een string wordt gezet die ik in andere delen van m'n programma weer kan gebruiken. Klinkt simpel, maar ik loop op 2 punten vast:

1 - Onderstaande code loopt in een eigen thread. Data van de ene thread naar de andere krijgen schijnt niet zo makkelijk te zijn en ik heb nog geen fatsoenlijke manier hiervoor gevonden. Iemand?

2 - Deze event wordt meerdere keren geraised per string die ontvangen wordt van de Arduino. Dus als m'n Arduino 'ditiseentest' verstuurt, komt het in brokjes aan en bij elk brokje wordt het event weer getriggered. Op zich is dat allemaal heel logisch, maar dat betekent wel dat ik in deze code heel moeilijk stukjes kan prutsen die de ontvangen data op een plek neerzetten waar de andere thread erbij kan (bijvoorbeeld in een textfile of op het clipboard...whatever).

Ik heb allerlei constructies geprobeerd om te wachten tot ik zeker weet dat alle data die ik wil hebben binnen is, maar niks werkt fatsoenlijk. Soms gaat het goed, maar vaak niet.

Heeft er iemand ideeën/suggesties/tips? Ik hoor het graag.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
        ReceivedText(SerialPort1.ReadExisting())    'Automatically called every time a data is received at the serialPort
    End Sub

    Private Sub ReceivedText(ByVal [text] As String)
        'compares the ID of the creating Thread to the ID of the calling Thread
        If Me.rtbReceived.InvokeRequired Then
            Dim x As New SetTextCallback(AddressOf ReceivedText)
            Me.Invoke(x, New Object() {(text)})
        Else
            Me.rtbReceived.Text &= [text]
        End If

    End Sub

[ Voor 90% gewijzigd door nephilimcrt op 10-11-2011 16:29 ]

Tapping progress on the shoulder and saying: "More forwards, please!".


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 12:01
1: Om op een veilige manier vanuit twee threads met eenzelfde buffer prutsen moet je deze eerst locken:
code:
1
2
3
4
lock( BufferLock )
{
 Do stuff with buffer
}

Om een goed beeld te krijgen van de mogelijk uitdagingen bij multithreaded software verwijs ik je naar een MSDN pagina, maar er is ook een heleboel te vinden over dit soort dingen die niet speciaal met .NET te maken hebben.


2:
Het idee is om op gezette tijden ( gepolled met een timer ( hint: ook te gebruiken voor de timeout bepaling ) of op basis van een DataReceived event of beide ) data uit je de poortbuffer te halen en samen te voegen met de data die je al had ontvangen. Op een gegeven moment bepaal je dat :
a - Een heel frame correct binnen is gekomen en je deze kunt verwerken. Vervolgens zorg je dat deze data uit de ontvangstbuffer wordt gehaald ( er zou dus meer data in die buffer kunnen staan dan dat ene frame )
b - Er een gedeelte van een frame binnen is, maar de rest niet meer komt ( timeout ) of er een heel frame binnen is, maar er klopt iets niet aan ( checksum, CRC whatever ). Clear je buffer.
c - Er in het geheel geen data binnenkomt ( ook timeout ).

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1