Shop OBEX P1 Docs P2 Docs Learn Events
VB 2005 and BS2 -- why won't the signal stop after I close connection? — Parallax Forums

VB 2005 and BS2 -- why won't the signal stop after I close connection?

tbaretbare Posts: 13
edited 2011-06-17 13:53 in BASIC Stamp
Here's a pretty basic question with probably a very easy answer.

I'm using the BoE - USB version

End goal: capture impressions from a press. The press will close a circuit each time there's an impression.

Currently, I'm simulating my press w/ one BS2 with the following code:
' {$STAMP BS2}
' {$PBASIC 2.5}

Flash: 'subroutine labeled Flash

DEBUG "*"
HIGH 1 'turn on
PAUSE 20
LOW 1 'turn off
PAUSE 313 'wait 1/3 second

GOTO Flash 'go back to the rest of the program
and the second BS2 is capturing it that signal like so:
' {$STAMP BS2}
' {$PBASIC 2.5}

btnwrk  VAR Byte
Btn     PIN 0

Main:
BUTTON Btn, 1, 255, 20, btnwrk, 0, No_Press : DEBUG "*" 'send * to output for each impression

No_Press:
GOTO Main 'Start back at the beginning
I've got a simple VB .NET app that i'm using for testing:
Imports System
Imports System.IO.Ports.SerialPort
Imports System.IO.Ports
Imports System.Threading

Public Class Form1
    Delegate Sub SetTextCallback(ByVal [text] As String) 'to change the text of Pause button w/o errors
    Public WithEvents COMPort As New System.IO.Ports.SerialPort
    Public Counter As Integer = 0
    Public PortAvailable As Boolean = False

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim ports As String() = GetPortNames()

        ' Check each available COM port for specified port
        Dim port As String
        For Each port In ports
            If port = "COM5" Then
                COMPort.PortName = "COM5"
                COMPort.BaudRate = 2400
                COMPort.DataBits = 8
                COMPort.Parity = IO.Ports.Parity.None
                COMPort.StopBits = IO.Ports.StopBits.One
                PortAvailable = True
            End If
        Next port
        If PortAvailable Then
        Else
            MsgBox("COM5 not available")
            End
        End If
    End Sub

    Private Sub SerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles COMPort.DataReceived
        Counter += 1
        Call updateText(Counter)
        If Counter Mod 200 = 0 Then
            COMPort.Close()
            COMPort.Open()
        End If
    End Sub

    Private Sub updateText(ByVal [Text] As String)
        If Me.lblComOut.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf updateText)
            Me.Invoke(d, New Object() {[Text]})
        Else
            Me.lblComOut.Text = [Text]
        End If
    End Sub



    Private Sub btnOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpen.Click
        If COMPort.IsOpen = False Then
            COMPort.Open()
        End If
    End Sub

    Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
        If COMPort.IsOpen = True Then
            COMPort.Close()
        End If
    End Sub
End Class
Currently, when I start the program up, I have to click start to start receiving the signal - perfect.

When I click "Stop," however, the counter in my program stops, but the red light keeps blinking telling me that the BoE is still sending the signal to the app. How do i tell the BS2 to stop running the code OR tell it to stop sending the info to through to the USB port?

Any help would be greatly appreciated - my more complex app is crashing when i hit stop if i keep sending a signal to the computer (impressions are still occurring), even if I 'Close' the connection...

I'm stuck.

TIA!

Comments

  • Mike GMike G Posts: 2,702
    edited 2011-06-13 15:25
    Turn off STAMP 1?

    Otherwise send a message to STAMP2 when you click the Stop button. Stamp 2 will need to look for the message and turn off/on accordingly.
  • tbaretbare Posts: 13
    edited 2011-06-13 17:07
    In theory, that would work, but in practice, i can't just "shut off the press" before i click "stop" in my app.

    additionally, if i do just "turn off the stamp," when i turn it back on, it goes right back to what it was doing. The only way to get it to stop is to unplug the usb cable, and plug it back in.

    what is the code to turn off / on the stamp? that is more or less what i'm looking for. :)
  • UnsoundcodeUnsoundcode Posts: 1,532
    edited 2011-06-14 08:40
    Hi, for serial communications I would say that the SEROUT and SERIN commands are much more flexible than DEBUG. If you want to receive information at the PC from the Stamp and also issue commands to the Stamp, such as stop start etc., you will need to devise some kind of simple protocol. Here is a link to some VB examples. http://forums.parallax.com/showthread.php?96973-VB-Express-to-Stamp-Template

    Jeff T.
  • tbaretbare Posts: 13
    edited 2011-06-14 14:34
    Thanks -- it looks like SERIN and SEROUT is what i'm looking for.

    As a complete self-proclaimed rookie, and being under a relative time-crunch, how would i do something like the following:

    IF SERIN ... [DEC 1]
    loop and output for each impression -- probably use similar BUTTON code that i currently have
    UNTIL SERIN [DEC 2]

    basically, once it receives a 1, look for button presses until it receives a 2, then wait for the next 1...

    Any push in the right direction (or flat out code) would be greatly appreciated... they're wanting this thing done as quick as possible (and they asked the new guy whom they knew had no experience with EEPROMs to do it..) :)

    EDIT:
    forgot to mention that i've looked at the Nuts & Volts chapter on Serin & Serout dmystified, as well as several other pages, but am running out of time on this.. I am trying to learn, promise! ;)
  • Mike GMike G Posts: 2,702
    edited 2011-06-14 14:46
    tbare wrote:
    what is the code to turn off / on the stamp? that is more or less what i'm looking for.
    The help file contains PBasic syntax and code examples. Here's a very simple loop.
    ' {$STAMP BS2p}
    ' {$PBASIC 2.5}
    
    result          VAR   Byte
    
    ' Initialize to ASCII "0"
    result = $30
    
    Main:
      ' Wait 5 seconds for inout from the PC
      ' jump to no data if we timeout
      ' else send an acknowledge "!" to let the PC know we
      ' received the data.
      ' Also echo the command.
      SERIN 16, 1021, 5000, No_Data, [result]
      SEROUT 16, 1021, ["!"]
      SEROUT 16, 1021, [result]
    
    No_Data:
      'Process if result equals "1" or ASCII $31
      IF result = $31 THEN
        SEROUT 16, 1021, ["HELLO", CR]
      ENDIF
    
    GOTO Main
    
    

    The VB logic needs to resend 0x30 (anything other than 0x31) if the acknowledge character is not received within some period of time (timeout). Once "!" is received then proceed to close the port. The STAMP does not handle parallel tasks. It could be working on something else when the VB app sends the stop command.

    When you open the port, send 0x31 until the "!" is received.

    Notice the PBasic logic is echoing the command. That's because you're listening for the Data_ Received event which is executing on a secondary thread. When you see a "!" the next byte is the last command. I'd roll up a listener (delegate) in the button close/open event and fire off an event when the "!" is received. You can also save the state of which button was pressed, then when you see the "!", read the state and handle.

    You could poll the serial port over using the SerialPort.DataReceived event. Polling will be a little easier.

    [Edit]: This is .NET source I used for testing cross platform serial port communication. It uses a timer to poll the serial port because Mono (on Linux) does not implement the DataReceived event. It's C# though.
  • Mike GMike G Posts: 2,702
    edited 2011-06-14 18:02
    tbare, I just noticed... this is bad.
        Private Sub SerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles COMPort.DataReceived
            Counter += 1
            Call updateText(Counter)
            If Counter Mod 200 = 0 Then
                COMPort.Close()
                COMPort.Open()
            End If
        End Sub
    

    I'm guessing that you're closing the port after 200 DataReceived events to clear the receive buffer? You're asking for trouble. Also there is not guarantee that impression count = DataReceived count. Consider reading the buffer and making a decision on the data received.
  • tbaretbare Posts: 13
    edited 2011-06-15 06:34
    yeah -- basically, after about 4000 impressions, my counter would go crazy, and start spinning like a stopwatch. after i closed and re-opened the port after every 200 counts, the problem went away... I think the buffer things is a better way to go. I'll look into that and see if i can clear the buffer on the stamp itself, rather than close / open.

    Thanks again for all the help! again, being a rookie in a new game is always difficult. :)
  • Mike GMike G Posts: 2,702
    edited 2011-06-15 08:34
    tbare, the buffer is in the PC not the STAMP. The STAMP does not have a buffer.

    Anyway, the .NET and PBasic code provided has what you need to complete the project. Did you, by chance, run the code?
  • tbaretbare Posts: 13
    edited 2011-06-15 10:43
    yeah - the code is working.. Thanks a ton.

    If I run into anything else, I'll post back.

    again, the help is MUCH appreciated!
  • tbaretbare Posts: 13
    edited 2011-06-17 07:05
    Hey, Mike -- In what version of VB did you write the C# code? I tried opening it in my VB 2005, but it said it was newer. Just wondering what version i need to install on a test machine so I can see what you did. I'm still having some issues getting my VB app to get the "!" back...

    EDIT - I got pulled onto a more pressing project -- i haven't been working on this non-stop since my last post.. just throwin' that out there... :)
  • tbaretbare Posts: 13
    edited 2011-06-17 07:42
    Never mind... i think i got that part... unfortunately, my app is still locking up after it closes -- I've put stops in to try to track down what's happening, but write after it closes the connection, it just freezes... I'll have to track it down.

    here's my stop code, though:
        Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
            Dim wrt As Byte = 0
            If COMPort.IsOpen = True Then
                While (True)
                    COMPort.WriteLine(wrt)
                    Thread.Sleep(50)
                    If COMPort.ReadExisting.ToString.Contains("!") Then Exit While
                End While
                COMPort.Close()
            End If
        End Sub
    

    and here's my BASIC stamp:
    ' {$STAMP BS2}
    ' {$PBASIC 2.5}
    
    result          VAR   Byte
    recdResult      VAR   Byte
    result2         VAR   Byte
    btnwrk          VAR   Byte
    Btn             PIN   0
    red             PIN   10
    green           PIN   11
    
    result    = 0
    result2   = 1
    
    ' Flash LEDs
    HIGH red
    PAUSE 250
    LOW red
    PAUSE 250
    HIGH green
    PAUSE 250
    LOW green
    
    Main:
      SERIN 16, 16468, 2, StartCounting, [DEC result]
      recdResult = result
      result = 0
      SEROUT 16, 16468, ["!"]
    
    
    StartCounting:
      'Process if result equals "1" or ASCII $31
      IF recdResult = 1 THEN
        LOW red
        HIGH green
        'SEROUT 16, 1021, [38]
    
        BUTTON Btn, 1, 255, 20, btnwrk, 0, No_Press : DEBUG "*" 'send * to output for each impression
    
        No_Press:
          GOTO Main
    
      ENDIF
    
     IF recdResult = 0 THEN
       LOW green
       HIGH red
       'SEROUT 16, 16468, ["!"]
     ENDIF
    
    GOTO Main
    
  • tbaretbare Posts: 13
    edited 2011-06-17 08:05
    So, if I add a MsgBox("blah") right before my COMPort.Close(), the msgbox takes various amounts of time to appear (b/w 1/4 second and 3 seconds), but it always closes, and never locks.

    Any thoughts as to what may causing this, and a good solution to bypass it w/out a msgbox? I've tried a thread.sleep(4000), but it doesn't help... I'm sure i could do a loop for a few seconds, but that seems like a bad work-around.

    Thanks!
  • Mike GMike G Posts: 2,702
    edited 2011-06-17 09:33
    tbare, the C# code (not VB) was written in Visual Studio 2010. I can convert it to 2005 if you like or you can download C# Express from Microsoft. The syntax will remain C#.
    Any thoughts as to what may causing this, and a good solution to bypass it w/out a msgbox?
    I'm guessing you're having a problem understanding multi-threaded applications. It's hard to tell with out seeing all your code.
    SERIN 16, 16468, 2, StartCounting, [DEC result]
    

    Change the baud to 2400 max (16780) and remove the DEC formatter from [DEC result].
    *The BS2, BS2e and BS2pe may have trouble synchronizing with the incoming serial stream
    at this rate and higher due to the lack of a hardware input buffer. Use only simple variables
    and no formatters to try to solve this problem.
  • tbaretbare Posts: 13
    edited 2011-06-17 10:59
    Here's the full testing code:
    Imports System
    Imports System.IO.Ports.SerialPort
    Imports System.IO.Ports
    Imports System.Threading
    
    Public Class Form1
        Delegate Sub SetTextCallback(ByVal [text] As String) 'to change the text with threadging
        Public WithEvents COMPort As New System.IO.Ports.SerialPort
        Public Counter As Integer = 0
        Public PortAvailable As Boolean = False
        Public StampReturned As String = ""
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Dim ports As String() = GetPortNames()
            Dim com As String = "COM4"
    
            ' Check each available COM port for specified port
            Dim port As String
            For Each port In ports
                If port = com Then
                    COMPort.PortName = com
                    COMPort.BaudRate = 9600
                    COMPort.DataBits = 8
                    COMPort.Parity = IO.Ports.Parity.None
                    COMPort.StopBits = IO.Ports.StopBits.One
                    PortAvailable = True
                End If
            Next port
            If PortAvailable Then
            Else
                MsgBox(com & " not available")
                End
            End If
        End Sub
    
        Private Sub SerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles COMPort.DataReceived
            If COMPort.ReadExisting.ToString.Contains("!") = False Then
                Call updateText2(COMPort.ReadExisting.ToString)
                Counter += 1
                Call updateText(Counter)
                If Counter Mod 200 = 0 Then
                    COMPort.DiscardInBuffer()
                    COMPort.DiscardOutBuffer()
                End If
            End If
        End Sub
    
        Private Sub updateText(ByVal [Text] As String)
            If Me.lblComOut.InvokeRequired Then
                Dim d As New SetTextCallback(AddressOf updateText)
                Me.Invoke(d, New Object() {[Text]})
            Else
                Me.lblComOut.Text = [Text]
            End If
        End Sub
    
        Private Sub updateText2(ByVal [Text] As String)
            If Me.TextBox1.InvokeRequired Then
                Dim d As New SetTextCallback(AddressOf updateText2)
                Me.Invoke(d, New Object() {[Text]})
            Else
                Me.TextBox1.Text = [Text]
            End If
        End Sub
    
        Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
            Dim wrt As Byte = 0
            If COMPort.IsOpen = True Then
                While (True)
                    COMPort.WriteLine(wrt)
                    Thread.Sleep(5)
                    If COMPort.ReadExisting.ToString.Contains("!") Then Exit While
                End While
                'Thread.Sleep(2000)
                If COMPort.IsOpen = True Then
                    MsgBox("here")
                    COMPort.Close()
                End If
            End If
        End Sub
    
    
        Private Sub btnGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGo.Click
            If COMPort.IsOpen = False Then
                COMPort.Open()
            End If
            Dim wrt As Byte = 1
            If COMPort.IsOpen = True Then
                While (True)
                    COMPort.WriteLine(wrt)
                    Thread.Sleep(5)
                    If COMPort.ReadExisting.ToString.Contains("!") Then Exit While
                End While
            End If
        End Sub
    
    End Class
    

    and the stamp:
    ' {$STAMP BS2}
    ' {$PBASIC 2.5}
    
    result          VAR   Byte
    recdResult      VAR   Byte
    result2         VAR   Byte
    btnwrk          VAR   Byte
    Btn             PIN   0
    red             PIN   10
    green           PIN   11
    
    result    = 0
    result2   = 1
    
    ' Flash LEDs
    HIGH red
    PAUSE 250
    LOW red
    PAUSE 250
    HIGH green
    PAUSE 250
    LOW green
    
    Main:
      SERIN 16, 16468, 2, StartCounting, [DEC result]
      recdResult = result
      result = 0
      SEROUT 16, 16468, ["!"] 'message received
    
    
    StartCounting:
      'Process if result equals "1" or ASCII $31
      IF recdResult = 1 THEN
        LOW red
        HIGH green
        'SEROUT 16, 1021, [38]
    
        BUTTON Btn, 1, 255, 20, btnwrk, 0, No_Press : SEROUT 16, 16468, ["*"] 'send * to output for each impression
    
        No_Press:
          GOTO Main
    
      ENDIF
    
     IF recdResult = 0 THEN
       LOW green
       HIGH red
     ENDIF
    
    GOTO Main
    

    Everything is working as expected now, except the stop - I'll look more into the multi-threading, as i agree that this is probably where i'm having my issues.

    like i said, the counter works as expected, just closing the connection doesn't... if I comment out the "COMPort.Close()" line, it works fine, still... but wouldn't that be bad form to leave the connection open like that? seems like a bad idea. I'd much rather open and close the connection for each use.

    As far as 2010, i'm pretty sure we have a copy i can use for testing under our ActionPack. I'll check. and converting from C# to VB.NET isn't an issue...

    Thanks again for all your help.
  • Mike GMike G Posts: 2,702
    edited 2011-06-17 11:36
    There are three glaring issues with your code.

    1) Counter is counting the number of DataReceived events not the number of impressions from a press!
    2) You're receiving data but not reading the data, just plopping it on the screen.
    3) The DataReceived handler is operating on a secondary thread while the btnStop_Click handler is invoked for the primary thread.

    Clicking the Stop button invokes a write to the serial port from the primary thread. The primary is put to sleep for 5ms but DataReceived handler is not asleep!
  • UnsoundcodeUnsoundcode Posts: 1,532
    edited 2011-06-17 11:54
    Great observations from Mike and such an easy thing to miss. One way you could handle it is to create a boolean variable that is a condition for all running processes you want to stop when you close the port.

    for example

    Dim Port_Closing As Boolean=True

    When you open the port:-

    Port_Closing=False

    In you Data Received event:-

    If Not Port_Closing Then

    ****Do your receiving here****

    End If

    And when you close the port:

    Port_Closing=True

    add a short delay here to allow time for everything to finish

    Jeff T.
  • tbaretbare Posts: 13
    edited 2011-06-17 11:57
    1) for each impression, the press closes a circuit simulating a button press - this is why i'm ussing BUTTON for this task - I've tested this on the press, and i get the desired result.
    2) This is my test app - I don't actually care "what" the information is during the DataRecieved handler - but if there is data recieved, i'm counting it as an impression, adding 1 to a variable, then every 250, writing it out to a database, with a final count written out when the stop button is pushed. - the data doesn't have to be perfect, but it needs to be closer than what they're getting now (which isn't counting any waste at all...)

    3) -- let's focus on this one, as it appears to be the bulk of the issue. I'm not very familiar with threading, so my question is how do you know that it's on a secondary thread? Any thoughts on how I would resolve this mistake?
  • tbaretbare Posts: 13
    edited 2011-06-17 12:07
    Jeff -

    I tried your suggestion w/ the PortClosing boolean, but it still locks, even with a 5 second sleep after i set the boolean to true and before i attempt to close the port.
    Private Sub SerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles COMPort.DataReceived
            If COMPort.ReadExisting.ToString.Contains("!") = False And PortClosing = False Then
                Call updateText2(COMPort.ReadExisting.ToString)
                Counter += 1
                Call updateText(Counter)
                If Counter Mod 200 = 0 Then
                    COMPort.DiscardInBuffer()
                    COMPort.DiscardOutBuffer()
                End If
            ElseIf PortClosing = True Then
                COMPort.DiscardInBuffer()
                COMPort.DiscardOutBuffer()
            End If
        End Sub
    
        Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
            Dim wrt As Byte = 0
            If COMPort.IsOpen = True Then
                While (True)
                    COMPort.WriteLine(wrt)
                    Thread.Sleep(5)
                    If COMPort.ReadExisting.ToString.Contains("!") Then Exit While
                End While
                PortClosing = True
                Thread.Sleep(5000)
                If COMPort.IsOpen = True Then
                    'MsgBox("here")
                    COMPort.Close()
                End If
            End If
        End Sub
    
        Private Sub btnGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGo.Click
            PortClosing = False
             {snip}
        End Sub
    

    i'm sure it's still the multi-threading issue - i just don't know how to resolve it.

    Thanks.
  • Mike GMike G Posts: 2,702
    edited 2011-06-17 13:53
    Working within your constraints from #18, the easiest thing to do is programmaticly remove the COMPort.DataReceived delegate when the Close button is clicked. Then repeatedly issue the close message and listen (polling) until you get the acknowledgement byte from the STAMP. Clean up your objects and close the port.

    Remember to assign the COMPort.DataReceived delegate to SerialPort_DataReceived when the Open button is clicked.
Sign In or Register to comment.