Wait for a Serial Port to respond

  • VB.NET

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von aTsiri.

    Wait for a Serial Port to respond

    Hi all,
    i don't know if i can ask in english, though it is a german forum
    I have a device connected to the serial port of my PC. I use the
    serialPort component in order to communicate with the device. I have to
    send 2 different strings of data to the serial port but the trick is
    that after sending the first string of data I have
    to wait for the serial port to respond. Then I have to check the
    response and decide whether I must send the second string or not. Also
    after sending the second string, I have to check also the response of
    the serial port.

    I tried to use an arbitrary amount of wait time between the sending of the two strings. Something like

    SerialPort.Write(...) ; Thread.Sleep(2000); SerialPort.Write(...);

    But the software failed when I tried it in a slower PC. So is there a
    way to replace the Thread.Sleep(2000) with a command that will wait
    until the serial port complete its response?

    Regards

    aTsiri
    Hi,

    the SerialPort has a DataReceived-Event. If your Device send any Data the DataReceived-Event got fired and you can check for the Data the device sended.

    VB.NET-Quellcode

    1. Private WithEvents Port As New SerialPort
    2. Sub SerialDataRecived() Handles Port.DataReceived
    3. End Sub
    Gruß K4RTOFF3L

    Das Problem zu erkennen ist wichtiger, als die Lösung zu erkennen, denn die genaue Darstellung des Problems führt zur Lösung. - Albert Einstein
    Do you mean if the device did execute the commands (Answer: it depends strong on your device. i cant help you here) or if you send your string completely (Answer: use the BytesToWrite-property of the SerialPort or let the device send a byte to confirm the message)?
    Gruß K4RTOFF3L

    Das Problem zu erkennen ist wichtiger, als die Lösung zu erkennen, denn die genaue Darstellung des Problems führt zur Lösung. - Albert Einstein
    Welcome to the forum.

    I don't understand post#3. The sequence is the following:
    • You send data to the serial port.
    • You get an answer by using the DataReceived-EventHandler. The answer is inside the e. Which e? Well, the EventHandler should look like this: Private Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs) Handles Port.DataReceived, see also the MSDN article
    • When you get the answer, you process it and probably send further data.
    A possibility could look like this:

    VB.NET-Quellcode

    1. Dim IncomingAnswerIsForFirstCommand = True
    2. Private Sub SendData()
    3. IncomingAnswerIsForFirstCommand = True
    4. Port.WriteLine(YourFirstCommand)
    5. End Sub
    6. Private Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs) Handles Port.DataReceived
    7. If IncomingAnswerIsForFirstCommand Then
    8. If ShallSendNextCommand(e) Then
    9. IncomingAnswerIsForFirstCommand = False
    10. Port.WriteLine(YourSecondCommand)
    11. End If
    12. Else
    13. ProcessSecondAnswer(e)
    14. End If
    15. End Sub

    ShallSendNextCommand should be a Boolean Function in which you decide, whether the next command should be sent.

    Of course there are better ways to achieve your desired behaviour, but because of the lack of knowledge I have with serial port programming I can't give you one. But users like @RodFromGermany surely have a better solution.

    ##########

    Oh, now I understand. Well, you have to check the manual of your device. Maybe it sends an EOF. Then the mentioned event will be fired. If not, then there should be another ending sign. This you have to get via the methods @K4RTOFF3L mentioned in the latter post.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

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

    My microcontroller accept commands with a semicolon at hte end, for example RH100; and responds with an answer with a semicolon too RH=100;
    The problem is that some times the microcontroller need some time to answer a command and thats is what i want to achiev, to be sure that the first packet of commands were delivered and answered and then i can go to the next packet. The microcontroller accepts sychronous commands , those are aswered immediately and asychronous , which maybe they need some time until the answer comes.

    i will post some of my code:

    1.send packet of commands to microcontroller with button_click event

    VB.NET-Quellcode

    1. Private Sub btnSend_Click(sender As Object, e As RoutedEventArgs)
    2. If IsNothing(Port) Then
    3. Return
    4. End If
    5. If Not Port.IsOpen Then
    6. Return
    7. End If
    8. If txtSendString.Text.Trim.Equals("") Then
    9. Return
    10. End If
    11. Dim s As String = txtSendString.Text.Replace("" & vbCr, "").Replace("" & vbLf, "").Replace(" ", "")
    12. Dim commands() As String = s.Split(New String() {";"}, StringSplitOptions.None)
    13. Dim i As Integer = 0
    14. Do While (i < (commands.Length - 1))
    15. SemicolonOut = (SemicolonOut + 1)
    16. i = (i + 1)
    17. Loop
    18. WriteToPort(s)
    19. End Sub


    here is how i read the answer :

    VB.NET-Quellcode

    1. Private Sub Port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
    2. While (CType(sender, SerialPort).BytesToRead > 0)
    3. instring = (instring + CType(sender, SerialPort).ReadExisting)
    4. ' MessageBox.Show(instring)
    5. End While
    6. Dim instrings() As String = instring.Split(New String() {";"}, StringSplitOptions.None)
    7. instring = instring.Substring((instring.LastIndexOf(";") + 1)).Trim
    8. Dim i As Integer = 0
    9. Do While (i < (instrings.Length - 1))
    10. Dispatcher.Invoke(Sub()
    11. Try
    12. Try
    13. CommDataTable.Rows(SemicolonIn)(1) = (instrings(i) + ";")
    14. Catch ex As System.Exception
    15. inError = (inError + 1)
    16. End Try
    17. Catch ex As Exception
    18. End Try
    19. End Sub)
    20. i = (i + 1)
    21. SemicolonIn = (SemicolonIn + 1)
    22. Loop
    23. setSemicolonInfo()
    24. End Sub


    the problem i am facing is when i have to send a packet in a loop by clicking a button

    a pseudocode would look like this

    Quellcode

    1. pseudocode
    2. Button_click
    3. for i =0 to number step 1
    4. SendToPort(commnds1)
    5. count the sendSemicolons (int number)
    6. Read the answer( as string) of the controller
    7. count the receivedSemicolons(Intnumber) from the answer string
    8. While (receivedSemicolons<sendSemicolons)
    9. wait untill the next answer comes from the microcontroller
    10. receivedSemicolons+=receivedSemicolons
    11. End While
    12. first packet completed go to next one
    13. SendToPort(commnds2)
    14. count the sendSemicolons (int number)
    15. Read the answer( as string) of the controller
    16. count the receivedSemicolons(Intnumber) from the answer string
    17. While (receivedSemicolons<sendSemicolons)
    18. wait untill the next answer comes from the microcontroller
    19. receivedSemicolons+=receivedSemicolons
    20. End While
    21. next i


    CodeTags corrected ~VaporiZed

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „VaporiZed“ ()

    Beside the fact, that your code could be much more easier*, maybe the Button-thing could be implemented like that:

    VB.NET-Quellcode

    1. Private AnswerReceived As Boolean = False
    2. Private Async Sub Button_Click(sender as Object, e As EventArgs) Handles Button.Click
    3. For Each Command In Commands
    4. SendCommand(Command)
    5. Await Threading.Tasks.Task.Run(Sub()
    6. Do Until AnswerReceived
    7. Loop
    8. End Sub)
    9. AnswerReceived = False
    10. If Not ContinueForLoop Then Exit For
    11. Next
    12. End Sub

    Then in your EventHandler you have to set the AnswerReceived To True and maybe the ContinueForLoop to False.

    * e.g. instring.Split(New String() {";"}, StringSplitOptions.None) -> instring.Split(";"c)
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @aTsiri Welcome to forum. :thumbup:
    You should use ths correct value for Your EndOfLine property:

    VB.NET-Quellcode

    1. SerialPortX.NewLine = ???
    With this value You will get in the DataReceived event the entire answer string. This string depends on Your hardware.
    docs.microsoft.com/de-de/dotne…?view=dotnet-plat-ext-3.1
    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!
    @RodFromGermany Thank you very much for your quick answer .i have testet it today the code you suggested me and it worked wonderfully.
    I am facing thought one more problem:

    How can i store the string from Data_Received method

    VB.NET-Quellcode

    1. Private Sub Port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
    2. instring = (instring + CType(sender, SerialPort).ReadExisting)
    3. End Sub


    in my main GUI (Windows Form Application) in a string and then edit this further.

    I will try to give an example:

    I am sending through a timer event tick a command to the controller

    SerialPort.write("?KI16;")

    when the DataReceived method fires answers in a string

    instring = (instring + CType(sender, SerialPort).ReadExisting) and takes the value of the answer of the microcontroller

    (instring="KI16=1;")

    i want this value to process it in my main GUI , thats why i need it to store it in a string value

    any idea how can i do that?
    So, you want to see this value in the GUI? Then just do it! Okay, because the DataReceived-EventHandler is in another thread, it's not obvious, but:

    VB.NET-Quellcode

    1. Me.Invoke(Sub() YourTextBoxWhichShallContainTheValue.Text = instring)

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Not exactly , i want to store the value in a string not to show it in a control

    can i do something like this :

    declare a string in my GUI

    VB.NET-Quellcode

    1. Private inputData as String=String.Empty


    and then call it like this :

    VB.NET-Quellcode

    1. Me.Invoke(Sub() inputData = instring)


    what do you think , could it work?


    Well, but you already have it in a string: instring. So?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    aTsiri schrieb:

    VB.NET-Quellcode

    1. Me.Invoke(Sub() inputData = instring)
    Use it so, Your string to store isn't a GUI control, thats why Invoke is not necessary:

    VB.NET-Quellcode

    1. Me.inputData = instring
    But, like @VaporiZed , which part of Your program uses that information?
    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!
    do you mean that in my main Thread i can call

    Me.inputData = instring

    VB.NET-Quellcode



    an i would read the data from the Data_received method?

    i am confused!
    I have posted earlier i want to make a decesion if the instring has one specific value.

    for example

    VB.NET-Quellcode

    1. Private Sub btnSendFile_Click(sender As Object, e As RoutedEventArgs)
    2. Try
    3. Me.inputData=instring
    4. if inputData="KI16=1;" then
    5. SerialPort.Write("!B;")
    6. else
    7. Catch ex As Exception
    8. End Try
    9. End Sub


    @aTsiri I Beleve that You have only one instance of a SerialPort, thats why you can do it so:

    VB.NET-Quellcode

    1. Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
    2. Dim instring = Me.SerialPort1.ReadExisting
    3. Me.inputData = instring
    4. Me.Invoke(Sub() Me.RichTextBox1.AppendText(instring & Environmen.NewLine)
    5. End Sub
    A RichTextBox needs an Invoke.
    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!

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

    Yes i have only one instance ok i will try it and i will let you know

    My prject is a cnc machine to control with a GUI Interface.The machine is connected via a microcontroller (RS232) with an USB Cable. My GUI hat buttons to control the movement of the cnc. A timer send every 100ms different commnds to interrogate different funktion of the machine, for example Status, Sensors signals, Position .
    So when i press a button to move an axis of the machine in one direction (x) i want the axis movement to stop if a sensor changes the value( from 0 to 1)

    Thats why i want to do something like i described above

    Whats the difference when u call the Serial Port Datareceived Event from another instance ?

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