Go Down

Topic: Sending Data to Serial Port through VB (Read 11358 times) previous topic - next topic

Robert Lee

Andy,

Sorry, that was a message left from my own test app for testing a Blue Tooth serial device. If you are seeing it then the port could not be opened, but it's masking the actual message.

Change it to:

MessageBox.Show(ex.tostring)

and it will show the complete message, but I suspect that it's not gaining access to the port. My thought is that with the loop at half a second, the open port is getting called multiple times. I'll put something together to stop it from trying to open it a second time.

The OpenPort routine creates a new instance of the SeriaPort class, and attempts to open the port specified. If it errors, it jumps to the "Catch" part of the code to catch the error.

No problem on the help, I just wish I had a copy of Rhino, Grasshopper in front of me to figure it out, oh well.


andyopayne

You can always download the trial version http://www.rhino3d.com/download.htm.  This one is the full version... however, it only allows you to save Rhino 25 times... You'll also want the Grasshopper plugin, which is free: http://grasshopper3d.ning.com/forum/topics/grasshopper-060019-available.  The nice thing about this setup... is that you can save as many Grasshopper files as you want... as long as you don't save the actual Rhino files (The Grasshopper interface is a completely different interface... so you save as many Grasshopper files .ghx without ever causing the Rhino eval version to expire...I think this would make sense if you install both packages).  I've actually written a 150 page manual about helping beginners get up to speed with the plugin and you can download it for free on my website: http://www.liftarchitects.com/downloads/.  Maybe this could help us figure this thing out.

andyopayne

Hi Rob.  I hope you had a great weekend.  I was wondering if you had had a chance to look at the issue that was creating the error trap when opening the port... that we talked about on Friday.

Robert Lee

Hey Andy,

Had a long weekend of work!

If you can post the scipt class (or email it to me, there should be a link at the bottom of the message), I will add a little code to only try to open the port if it isn't open. That way I'll be changing your current version, instead of giving you a bunch of pieces of code to splice in.

Rob

andyopayne

Hi Rob,
Sorry to hear you had to work all weekend :(  I actually have the latest file at home (I don't have it here at work).  I can post/email you the current script file as soon as I get home in a few hours (probably around 6:30 PST).  Thanks again for your help.

andyopayne

Rob, here is the code where I last left off.  Basically, I'm feeding a variable input called StepNumber (which is either a 0,1, or 2) to call one of the three different Case functions.  We figured out that it's getting stuck when the Timer component tries to rebuild the solution that it's trying to continually re-open the port.  

Code: [Select]
 Sub RunScript(ByVal StepNumber As Integer)
 
   Select Case StepNumber
     Case 0
       OpenPort()
       print("Port has been opened")
     Case 1
       print(storedvalue)
     Case 2
       ClosePort()
       print("Port has been closed")
   End Select

 End Sub

#Region "Additional methods and Type declarations"

 ' I wrapped this in a class, but you would need to work it into the grasshopper class

 Private WithEvents CPort As IO.Ports.SerialPort      'port with events
 Private TempBuffer As String = String.Empty        'holds the input between events until we get a new line
 Private Delegate Sub HandleComPortDelegate()      'allows com port to run in a background thread without blocking
 Private StoredValue As String = String.Empty      'holds value from port until you call for it

 Sub OpenPort()            'called early in the app, before the port is ever needed. This attaches the port, and opens it to listen.
   CPort = New IO.Ports.SerialPort

   Try
     CPort = My.Computer.Ports.OpenSerialPort("COM3", 9600)
   Catch ex As Exception
     MessageBox.Show("no port")
   End Try
 End Sub

 'this would be better as a property, but I don't know how Grasshopper handles properties
 Function GetValue() As String
   Return StoredValue
 End Function

 'this is fired by the port when it has data
 Private Sub CPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles CPort.DataReceived
   Dim HndlComPort As New HandleComPortDelegate(AddressOf HandleComPort)             'declare thread
   HndlComPort.Invoke() ' invoke a new delegate to digest the events

 End Sub

 Private Sub HandleComPort()      'background handler for com port

   TempBuffer &= CPort.ReadExisting  'append into tempbuffer
   If (Strings.Right(TempBuffer, 2) = vbCrLf) Then        'if end of item            'this may need to be changed, since Arduino's println may use cr or lf only. I can't remember at this point.
     StoredValue = TempBuffer               'offload
     TempBuffer = String.Empty               'reset
   End If

 End Sub

 'should be called when the port is no longer needed.
 Private Sub ClosePort()

   If CPort.IsOpen = True Then
     CPort.Close()
   End If
   CPort.Dispose()

 End Sub
#End Region

Robert Lee

Andy,

Give this a shot. I moved the port creation so that it is created during the class creation, and added a check so that we don't call open if we are already open.

Code: [Select]


     Sub RunScript(ByVal StepNumber As Integer)

           Select Case StepNumber
                 Case 0
                       OpenPort()
                       Print("Port has been opened")
                 Case 1
                       Print(StoredValue)
                 Case 2
                       ClosePort()
                       Print("Port has been closed")
           End Select

     End Sub

#Region "Additional methods and Type declarations"

     ' I wrapped this in a class, but you would need to work it into the grasshopper class

     Private WithEvents CPort As New IO.Ports.SerialPort        'port with events
     Private TempBuffer As String = String.Empty            'holds the input between events until we get a new line
     Private Delegate Sub HandleComPortDelegate()  'allows com port to run in a background thread without blocking
     Private StoredValue As String = String.Empty  'holds value from port until you call for it

     Sub OpenPort()              'called early in the app, before the port is ever needed. This attaches the port, and opens it to listen.
           Try
                 If CPort.IsOpen = False Then
                       CPort = My.Computer.Ports.OpenSerialPort("COM3", 9600)
                 End If
           Catch ex As Exception
                 MessageBox.Show("no port")
           End Try
     End Sub

     'this would be better as a property, but I don't know how Grasshopper handles properties
     Function GetValue() As String
           Return StoredValue
     End Function

     'this is fired by the port when it has data
     Private Sub CPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles CPort.DataReceived
           Dim HndlComPort As New HandleComPortDelegate(AddressOf HandleComPort)             'declare thread
           HndlComPort.Invoke() ' invoke a new delegate to digest the events

     End Sub

     Private Sub HandleComPort()        'background handler for com port

           TempBuffer &= CPort.ReadExisting  'append into tempbuffer
           If (Strings.Right(TempBuffer, 2) = vbCrLf) Then        'if end of item            'this may need to be changed, since Arduino's println may use cr or lf only. I can't remember at this point.
                 StoredValue = TempBuffer             'offload
                 TempBuffer = String.Empty             'reset
           End If

     End Sub

     'should be called when the port is no longer needed.
     Private Sub ClosePort()
           If CPort IsNot Nothing Then
                 If CPort.IsOpen = True Then
                       CPort.Close()
                 End If
                 CPort.Dispose()
           End If
     End Sub
#End Region



andyopayne

Hi Rob,
I tried out the script you wrote.  When I first run the script, I make sure that the StepNumber is set to 0 to open the port.  Then I switch the StepNumber to 1... but nothing happens.  I did put in another print line just to see if it was calling Step 1... and it does make it into the loop... but it isn't returning any values for the StoredValue from the COM port.  So, I then switch the StepNumber to 2 to close the port.  When I retrace the steps (meaning... I've connected a slider to the input... so to get back to 0, I first have to slide to 1 and then to 0 to open the port)... The funny thing is, that when I get back to 0... I get that same error message that says "No Port".  Which seems odd because you would think that the port would have been closed when I took the slider up to 2.  Perhaps there is an error in the close port method too.  Do you have any ideas?  Also, why would it not be reporting whatever the StoredValue is the first time I input the number 1 into the StepNumber?  Any ideas?  

andyopayne

Actually! It worked... I don't know what the difference was... but I unconnected the Arduino and closed the program... and re-tried everything... Only this time when I opened the file... it works.  I'm getting a stream of data from the potentiometer... just like the serial monitor.  It's great...
I did notice something odd though.  When I go and update the code (I wanted to take the extra print line out of the VB script)... when I return to the Grasshopper canvas... even though the StepNumber is set to 1... I'm not getting anything coming out of the Out box... So, I plugin the number 2 to close the port... and when I try to re-open the port by plugging in the number 0... I get that error message again, saying "No Port".  Then, I can no longer get the streaming numbers back... unless I close the entire program and re-launch it.  Can you think of any ideas why this would be?

andyopayne

Also... I have one other follow up question... I noticed that the numbers coming in over the COM port are some value between 0-1024 (this makes sense... I have a potentiometer coming in so it would be read as a 10bit number)  However, I've noticed that the data type is a "string" and not an integer... But, this is a little unfortunately because I'd like to be able to take those integers and feed them into some component that requires a number... but since Grasshopper thinks the data coming in is a String... it can't read the numbers... How would I modify the code to convert those strings to integer values?  Thanks again for all the help... I feel like we made huge progress (even though there are still some kinks to work out).  

Robert Lee

Andy,

It sounds like the port is not being closed, or released properly.
Most likely it is related to the way that the port object handled when Grasshopper releases the object to allow you to edit it, but it could be that the port can't be recreated properly.

You could change the "MessageBox.Show("no port")" line to "messagebox.show(ex.tostring)" or ex.message and you should get a little more descriptive error.


Robert Lee

How are you trying to get the numbers? I would assume that the print function returns a string value, but depending upon what Grasshopper expects, they could certainly be converted.

andyopayne

It's really weird... Sometimes I get that error message that I just told you about... but I unplugged the Arduino again... and closed Rhino... I restarted everything... and now everything is working perfectly.  I changed the process of feeding the StepNumber... Before I was using a slider so that I either had to go from 0,1, and then 2... but to get back to 0 to open the port... you had to first pass the number 1 to the code... which I think was part of the problem... Now, I just have three parameters, each containing a single number.  Now, I just connect the first parameter to open the port... Then I connect the Get Value parameter (number 1).  Then I close the port... only now, I just re-connect the Open Port parameter and re-trace my steps... and it seems to work.  
So, as for the string to number conversion...  The string is just coming in because we have defined the "Private StoredValue As String = String.Empty".  Wouldn't we want to change this part of the code so it thinks the data coming in isn't a string but instead a number?  I can't think of a way off the top of my head for Grasshopper to convert the string to a number after the fact.  I will play around with some different techniques... but if you think changing the VB code to read the numbers directly... let me know.

Robert Lee

Try changing your print statement to this:

Print(CInt(Val(StoredValue))) That will convert it to an integer before it sends it back to Grasshopper.

andyopayne

This is great... It's totally working... I rigged up my arduino to read a simple photocell setup... I used your trick to convert the string to a number before sending it to the text panel... And then I just feed the data from a text panel to drive the radius of a circle... I know that's pretty simple... but it totally works.  Below is a screenshot of the definition setup and I've set the Timer interval to 100ms so the data stream is fairly smooth (I think 50ms is the lowest I can go in Grasshopper).  So, I just shine a flashlight on the photocell... and the circle changes shape... My next step is going to be reading in the data from a wii remote (I've already gotten the wii remote to drive a pan/tilt servo... but I wanted to try to recreate the motion in Grasshopper).  But, I think the steps should be fairly straight forward.  Thanks again for all of you help... I never would have made it this far without your help.  If your ever out in the Bay Area... definitely let me know... I owe you a beer (or several).

http://www.liftarchitects.com/storage/research/Arduino%20to%20Grasshopper_working01.jpg

Go Up