Arduino Forum

Forum 2005-2010 (read only) => Software => Interfacing => Topic started by: andyopayne on Aug 17, 2009, 07:08 am

Title: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 17, 2009, 07:08 am
I am trying to send some information to an Arduino board by using a VB.net script... only, I have to admit I'm a bit of newbie in VB and I am a little stuck.  I have set up a boolean toggle so that if the toggle is set to true, then it should send an 'H' to the board and set Pin 13 to High.  If the toggle is set to false, then the script will send an 'L' to the board and turn pin off.  I can load the Physical Pixel example to the board so that when it receives either of these letters, it will either turn the Pin on or off... so basically, what I need to do is to somehow send a letter to the board... through VB.  But, I'm a little lost on where to even begin.  
This link was suggested as a starting place (http://support.microsoft.com/kb/823179/)... but I'm not actually using Visual Studio or Express.  I'm using a plug-in (called Grasshopper) for a 3d software package (called Rhino).  You have the ability to write your own custom components through VB, which is where I got the idea to send data directly from this plugin to the Arudino.  If someone could help me with some basic instructions on where to begin, or how to do this simple example... I would greatly appreciate it.  Cheers, Andy

This is the code that is setup in the plugin.  The message that is fed into the component is either the letter 'H' or 'L'.  This letter should be sent when the activate boolean value is set to True.

Code: [Select]
 Sub RunScript(ByVal message As String, ByVal activate As Boolean)
   If activate Then
     'If boolean set to true, then send message to Arduino...
   End If
   
 End Sub
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 17, 2009, 03:03 pm
The info on the second article of the link is still what you will need to do.

In your routine you will need to create a serial port, and send the data over it.

something like this should work. You'll have to attach to the serial port that your Arduino is on.

Code: [Select]


     Sub RunScript(ByVal activate As Boolean)
           Dim data As String = String.Empty

           If activate = True Then
                 'If boolean set to true, then send message to Arduino...
                 data = "H"
           Else
                 data = "L"

           End If

           Using com1 As IO.Ports.SerialPort = My.Computer.Ports.OpenSerialPort("COM1")' change to your port
                 com1.WriteLine(data)
                 com1.Close()
           End Using

     End Sub




On a side note, if your intent is to learn VB.NET, you would be far better off in Visual Studio. The express versions are free.
Title: Re: Sending Data to Serial Port through VB
Post by: geek314 on Aug 17, 2009, 09:34 pm
How would I convert the VB.net code to VBS
I think I know how to convert "com1 as IO." but not "IO = My.Computer"

Would these even work in vbs?
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 17, 2009, 10:18 pm
If you are running .NET, it's not VBS. VBS is based on older (VB6 and prior) languages.

I guess we really need to figure out what language the scripting tool is using. If it is using .NET, the code that I posted should run just fine (although you may need some includes that Visual Studio sets up by default). If it is using VBS, then you'll want to search for a VBS sample for com port.

Can you post some links to the Rhino / Grasshopper tools?
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 18, 2009, 05:08 am
This is great!  Your sample code worked perfectly.  I know this was a very basic example, but my main hurdle had been how to get any data transmitted from Grasshopper to the Arduino.  I can now change the toggle and turn on the LED... very dumb... but now, the sky is the limit.  
BTW, here is a link to the main Grasshopper page.  http://www.grasshopper3d.com/ and here is a link to the scripting page in Grasshopper: http://www.grasshopper3d.com/page/scripting-and-code-tutorials.  
As I mentioned, what's nice about Grasshopper is that it allows you to set parameters that control 3d objects.  Now, that I can successfully talk between Grasshopper and the Arduino, I can easily design something in 3d in the computer and have it also output the data in realtime to a board which will also control the physical environment.  I'm much more proficient in Grasshopper (vs. VB.net) so if you have any questions regarding the interface, don't hesitate to ask.
Thanks again.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 18, 2009, 06:08 pm
I have a follow up question.  I have successfully sent data from Grasshopper to the Arduino through VB.  But, what if I wanted to reverse the flow of information.  What if I wanted to send data from the Arduino to Grasshopper (through VB).
Here's a simple example.  Let's say I have a photocell hooked up so that when I turn on the serial monitor, I get a stream of voltage information that is coming into the computer.  How would I read that information in Grasshopper (again using VB).  The problem in the past with Grasshopper has always been with reading information into the program... It has no problem streaming data out.  But, it only refreshes the algorithm if something has changed in Rhino or in Grasshopper... and since nothing has really changed in the Grasshopper definition or in the Rhino viewport, it doesn't know to refresh the solution, and thus get new information in from an external file.  In the recent release of Grasshopper (just 2 weeks ago) there is a new timer component that will refresh the solution based on any time interval that you give it.  I think MaxMSP has something very similar (I think it's called the Metronome).  So, I think I can use this component to automatically refresh the solution every (1 sec for example) to retrieve the new information being sent from the Arduino.  My main question is how to script it in VB to read this information.  I may not have been clear, but if you have any suggestions, I would really appreciate.  Thanks again for your help so far!
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 18, 2009, 09:07 pm
I don't know if you can call functions from Grasshopper, but if you can then the following should read one line from the serial port. You could call it whenever you want to retrieve data from the port.

Code: [Select]


Function ReceiveSerialData() As String
   ' Receive strings from a serial port.
   Dim returnStr As String = ""

   Using com1 As IO.Ports.SerialPort = _
           My.Computer.Ports.OpenSerialPort("COM1")
       Do
           Dim Incoming As String = com1.ReadLine()
           If Incoming Is Nothing Then
               Exit Do
           Else
               returnStr &= Incoming & vbCrLf
           End If
       Loop
     com1.Close()
   End Using

   Return returnStr
End Function


Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 19, 2009, 04:13 pm
I tried putting in the code to read the photocell data... but I'm still a little stuck.  I put the snippet of code in the "Additional Methods and Type Declarations Area" because I was getting error messages if I put the code in between the Subs.  I don't get any error messages (which is usually a good sign) but I'm also not getting a read out of the photocell values that are being shown in the Arduino Serial Monitor either.  I tried putting a print (returnStr) into the code, but nothing happened.  Here's what my code looks like.  Please let me know if I'm way off base, or if there is something I'm missing.  Thanks.

Code: [Select]
 Sub RunScript(ByVal x As Object, ByVal y As Object)
   
 End Sub

#Region "Additional methods and Type declarations"
 Function ReceiveSerialData() As String
   ' Receive strings from a serial port.
   Dim returnStr As String = ""

   Using com3 As IO.Ports.SerialPort = _
       My.Computer.Ports.OpenSerialPort("COM3")
     Do
       Dim Incoming As String = com3.ReadLine()
       If Incoming Is Nothing Then
         Exit Do
       Else
         returnStr &= Incoming & vbCrLf
       End If
     Loop
     com3.Close()
   End Using

   Return returnStr
 End Function
#End Region
End Class
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 19, 2009, 04:31 pm
Ok... I may have posted prematurely... although I still haven't figured it out.  I realized that I had defined the Function ReceiveSerialData(), but I never called that function to actually go and get the data.  So, I put line in between the Subs that called the ReceiveSerialData() function, only now I'm getting an error message that says, "Script exception: Access to the port 'COM3' is denied."  Which is really weird because I was definitely able to send data from Grasshopper to the COM3 port in our previous example.  Have I set up the code correctly?  
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 19, 2009, 10:16 pm
The code looks fine, but it's hard to say what is going on since I'm not really sure how grasshopper calls the code in each routine / class / sub. The error looks like a generic error message, instead of the full message that .NET returns which would give more info.

You don't have something else talking to COM3 at the same time do you? Like another monitoring app, Arduino, or the routine that writes to the port.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 19, 2009, 10:31 pm
No, nothing else was talking to the COM3 port.  In fact, that was the only COM port available.  I should say that when I did try to run the code (twice), it actually froze the Rhino application.  As if it were in a continuous loop.  Is there a chance that I didn't call the receive function properly?  How should the code between the subs look like?
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 19, 2009, 11:01 pm
By "How should the code between the subs look like?" you are talking about the # region area.

The region is just a editing tool for the Visual Studio IDE that allows you to group routines together, and fold the code. It is removed by the compiler.


We could try trapping the error and displaying it directly, instead of allowing grasshopper to catch the error. Try this function instead.

Code: [Select]


     Function ReceiveSerialData() As String
           ' Receive strings from a serial port.
           Dim returnStr As String = ""

           Try
                 Using com3 As IO.Ports.SerialPort = My.Computer.Ports.OpenSerialPort("COM3", 9600)
                       Do
                             Dim Incoming As String = com3.ReadLine()
                             If Incoming Is Nothing Then
                                   Exit Do
                             Else
                                   returnStr &= Incoming & environment.newline
                             End If
                       Loop
                       com3.Close()
                 End Using

           Catch ex As Exception
                 MessageBox.Show(ex.ToString)
           End Try

           Return returnStr
     End Function

Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 20, 2009, 08:11 am
I appreciate all of the help you have been giving me, as I am really learning a lot from these examples.  I tried the code snippet you posted, but have gotten a longer error message (as expected, I think).  I've also posted a screen shot of both the error message that I get when I call the function and also how my screen looks when I have both the Serial Monitor and the Grasshopper VB interface open at the same time.  Again, I'm not sure what I'm doing wrong, and the error message looks quite confusing, but if you have any ideas, I would greatly appreciate it.  Thanks again.  

(http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino%20VBerror.jpg)
(http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino%20VB01.jpg)
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 20, 2009, 01:36 pm
Where have you posted the screen shots at?
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 20, 2009, 04:09 pm
You can't see them?  Huh, I uploaded them to my website and posted the URL.  When log in to the forum and check this thread, they show up.  Hmm...  What if I just attach the URL.  Try this.
http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino%20VB01.jpg
http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino%20VBerror.jpg
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 20, 2009, 04:33 pm
That's weird, Before looking at the images on your site, I could not see them on the forum, now they show up!?

It looks like you have the Arduino serial monitor attached to the com port. Only one device on the system can use the port, so if Arduino is attached, you won't be able to get Grasshopper attached. The access denied error is telling us the same thing: that something else is already talking to the serial port.

The other thing I noticed is that there was an error message on the window in the background (near the bottom center of the screen). It is referring to a recursive call. I'm not sure if this is related to the issue you have that seems like the code loops forever, but it could be.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 20, 2009, 04:55 pm
So, does that just mean that the Serial Monitor has to be closed for the script to work?  That way nothing else is talking to the COM port?
As for the other error message that you pointed out... That is a warning in the Grasshopper Interface.  If something isn't working correctly, then Grasshopper will give you a warning message in that area.  So, I do think there is still something in the code that is causing it to have a continuous loop.
So, is this what I should do (when I get home tonight).  Make sure the Arduino is connected, but do NOT turn on the Serial Monitor.  Then, launch Grasshopper and put in the original code (see below).  Then call the function (between the subs) using this code "ReceiveSerialData()".  Does that make sense?

Code: [Select]
Sub RunScript()

 End Sub

#Region "Additional methods and Type declarations"
 Function ReceiveSerialData() As String
   ' Receive strings from a serial port.
   Dim returnStr As String = ""

   Using com3 As IO.Ports.SerialPort = _
       My.Computer.Ports.OpenSerialPort("COM3")
     Do
       Dim Incoming As String = com3.ReadLine()
       If Incoming Is Nothing Then
         Exit Do
       Else
         returnStr &= Incoming & vbCrLf
       End If
     Loop
     com3.Close()
   End Using

   Return returnStr
 End Function
#End Region
End Class

Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 20, 2009, 05:20 pm
Exactly; the serial monitor is connecting to COM3 to listen to the Arduino. The rest of the code should work as you have it, barring whatever recursive error you have in Grasshopper.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 20, 2009, 05:45 pm
Great.  I will try it out tonight and let you know how it works.  Thanks again!
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 22, 2009, 06:55 am
Well... I've tried this several different ways, and I'm still getting a continuous loop error.  I made sure the Serial Monitor wasn't running and I tried a slightly different version of the code to try to get out of the continuous looping problem.  But, it still locks up every time I call the function.
Here's how the Grasshopper side of things is set up.  I have created a boolean toggle that feeds either a true or false value into the VB script component.  I have also connected a Timer Component which should refresh the solution at any given time interval... in this case every 1 sec.  So, ideally... when you flip the toggle to True, the script will call the function and retrieve whatever data is coming into the COM3 port.  This will be refreshed every 1 sec.  However, it's getting stuck.  Here is the modified code I tried, but it still isn't working.  I've also posted a picture of the setup.  If you have any more ideas, I would really appreciate them.  Thanks for all the help along the way.  I feel like we are really close.  Let me know if the images don't show up again :)

Code: [Select]
 Function ReceiveSerialData() As String
   ' Receive strings from a serial port.
   Dim returnStr As String = ""

   Using com3 As IO.Ports.SerialPort = _
       My.Computer.Ports.OpenSerialPort("COM3")
     Do
       Dim Incoming As String = com3.ReadLine()
       If Incoming Is Nothing Then
         Exit Do
       Else
         returnStr &= Incoming & vbCrLf
         com3.Close()
         Return returnStr
       End If
     Loop
     
   End Using

   Return returnStr
 End Function


(http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino%20VB02.jpg)
(http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino%20VB03.jpg)
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 22, 2009, 07:00 am
For some reason I can't see the images again.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 22, 2009, 04:03 pm
Let's see if just posting the URL works this time.  Try these links.  If you still can't see them, perhaps I could email you directly.  Is there away to do that through this forum?
http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino%20VB02.jpg
http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino%20VB03.jpg
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 22, 2009, 04:44 pm
Well, I don't see anything right off hand that looks wrong. The only thing that might cause issues is calling the ReceiveSerialData function every second. If it has not finished the last read it could still be running, which would most likely throw an error.

Can you subscribe to events in Grasshopper? My normal method of using com ports is to create the port, attach an event handler to the "data received event" and open it. Then whenever the com port receives data it raises an event, effectively letting me know that it has happened.

My other suggestion would be to download VB express http://www.microsoft.com/express/vb/Default.aspx to use for debugging your VB code. Once you are sure of how the VB code is going to run, you can put it back into Grasshopper.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 22, 2009, 05:06 pm
Is it possible that the error could be on the Arduino side?  I'm using the AnalogeInSerial example sketch (code is below).  It's reading the voltage input every 10 ms.   The Grasshopper definition is trying to listen to the COM port every N ms (50 ms is the smallest interval I can go in Grasshopper).  I can obviously change the Arudino interval to match the Grasshopper interval... but they will probably never be completely in sync since they are starting at different points in time.  The Grasshopper Timer is supposed to be a asychronous timer, so I wouldn't think that a mismatched time interval would really matter.  But, could I be making a mistake in using the AnalogeInSerial example to send the data to Grasshopper.  Is there a better way just to test whether or not I can actual receive the data in Grasshopper?  Thanks for the quick replies.
Code: [Select]
Analog input, serial output

Reads an analog input pin, prints the results to the serial monitor.

The circuit:

* potentiometer connected to analog pin 0.
  Center pin of the potentiometer goes to the analog pin.
  side pins of the potentiometer go to +5V and ground

created over and over again
by Tom Igoe and everyone who's ever used Arduino

*/

void setup() {
 Serial.begin(9600);
}

void loop() {
 // read the analog input into a variable:
  int analogValue = analogRead(0);
  // print the result:
  Serial.println(analogValue);
  // wait 10 milliseconds for the analog-to-digital converter
  // to settle after the last reading:
  delay(10);
}
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 22, 2009, 05:18 pm
In this type of setup, where you are sending a fairly quick stream of data, you are going to want to open the serial port once, and leave it open. I really don't know what Grasshopper is capable of, but it appears that it creates a class and can call routines in that class. For this to work I would think that you will need to be able to have Grasshopper handle an event, or at least open the serial port when the app starts, and then call a function that returns a variable stored when the serial port receives data. I'll work up a code sample and post it here. You may get more help from a Grasshopper forum as far as how to integrate the sample, but once you know what works it should be easier to get help with.

Again, I think that it would be easier to debug, and test the code from within VB, and then integrate it once you know it works.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 22, 2009, 05:30 pm
Thanks Robert.  I have also been sending questions to one of the Rhino developers about this problem... so hopefully we will be able to come up with a solution.  But, thanks for the input and I look forward to the example.  I had wondered about opening and closing the port each time the function was called.  It seems like you could put the Opening portion of the code in the If/Else part of the code... so that if the boolean toggle is switched to True, then the port will be opened and then call the Receive data function.  If the toggle is switched back to False, then the COM port is closed.  Would that work?
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 22, 2009, 05:31 pm
Another thought; do you need all of the data that you are trying to send from the Arduino, or just whatever the current value is when you are ready for it?

Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 22, 2009, 05:32 pm
I think whatever the current value is would be just fine.  That way, you could just change the Timer Interval in Grasshopper to adjust how quickly you get your data.
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 22, 2009, 05:36 pm
It really depends upon how Rhino / Grasshopper compiles and maintains the class in memory. If it is done once (at startup, or first reference), then you could theoretically change the scope of the com port object, and keep it alive between calls (what my sample will do). If they recompile the class whenever it is referenced, then you can't do that.

Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 22, 2009, 06:08 pm
Here's a sample of what you would want to accomplish. I haven't tested this, but will load the sample to my Arduino and refine it in a bit. It should be very close to what you want though. The big question is if you can call other routines in the class, or only RunScript.

One other point, in the code above you are printing "not receiving data" when the boolean is false, but I don't see where you are using the data from the serial port when the boolean is true. I think that you would want to use "Print(ReceiveSerialData())" to send the serial data back to the app.


Code: [Select]


Public Class port

' 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 Class


Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 22, 2009, 06:21 pm
Thanks Robert... I have to run out the door, but I will try your code as soon as I get back in tonight.  Thanks again.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 24, 2009, 06:56 pm
Hi Robert,
I have been trying your code... but am still getting stuck somewhere... probably because of my experience with VB.  I was assuming that I should put the code snippet in between the #Region area (like I had before) and then call the different subs/functions when I activate the boolean toggle.  So, the code looked like this when I put it into Grasshopper (see below).  The problem is that I'm getting a lot of error messages saying that OpenPort, GetValue, ...etc. are not declared... so the script wont even run.  I also had a question about the Class comment you added... saying that I would need to "work it into the grasshopper class".  How exactly do I do that?  I know it's not an easy question since you are unfamiliar with the program... but I do appreciate your help with all of this.  

Code: [Select]
 Sub RunScript(ByVal activate As Boolean)
   If activate Then
     Call OpenPort()
     Call GetValue()
     Call CPort_DataReceived()
     Call HandleComPort()
     print(StoredValue)
   Else
     Call ClosePort()
     print("not receiving data")
   End If
 End Sub

#Region "Additional methods and Type declarations"
 Public Class port

   ' 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 Class
#End Region
End Class
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 25, 2009, 03:38 pm
Quote
saying that I would need to "work it into the grasshopper class".  How exactly do I do that?


All I meant was that you would have to take the class I gave you, and cut it into pieces and put it back into the grasshopper script class.

You got most of it spliced in correctly, but I think we need to determine a couple of things about Rhino/Grasshopper before we can do much more.

You can get rid of most of the errors by removing the "Public Class port" and "end class" (the one before #End Region ) lines inside of the code you pasted. That's going to bring more errors though.
You will have to remove the calls to CPort_DataReceived and HandleComPort as they are used internally in the class and not meant to be called externally.

The real important question now is: can you call different routines inside the script class (from Grasshopper), or do you have to always call the run script subroutine? Once I know that, I can provide the last bits of guidance to try to get this running!

Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 25, 2009, 05:25 pm
Thanks for your explanation.  It is very helpful.  I did make the changes you mentioned and it's a lot happier.  The good news is, it's no long stuck in an endless loop.  I can switch the boolean toggle to "True" and (nothing comes out of the out box) but at least it doesn't freeze the app.  So, when I did make those changes... I got one error message that said, "Script exception: Object reference not set to an instance of an object".  I'm a little confused about that because I nothing in the class is being transferred ByRef... instead everything is being called ByVal.... so, I'm not exactly sure what the error message is talking about.  Also, I will try to email one of the developers about whether or not you can call different routines inside the script class... I have a feeling that you always have to call the run script routine (pretty sure, but I will double check).  Thanks again for your help.
Here is the most current code that I have put in my GH definition:
Code: [Select]
Sub RunScript(ByVal activate As Boolean)
   If activate Then
     Call OpenPort()
     Call GetValue()
     print(StoredValue)

   Else
     Call ClosePort()
     print("not receiving data")
   End If
 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
End Class
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 27, 2009, 04:45 pm
Hi Rob.  I have placed a few emails and a post on the Grasshopper forum about whether or not you can call different routines inside the script class.  But perhaps you could explain what the difference in the two methods would be?  Maybe I could try out both options and if one of them works then we know which way the script is called inside Grasshopper.  I think we're really close.  Thanks again.
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 27, 2009, 06:18 pm
No problem.

Here's the basics of what you need to try to accomplish with the class I sent you.

You need to call OpenPort once, preferably at the start of the program. This opens the port and listens to the Arduino.

at this point the class puts the port into a background thread, and attaches an event handler to it, returning control to your app. whenever the port gets new data, it raises an event that updates the variable in the grasshopper script class.

You need to call GetValue in the timer from grasshopper whenever you want to get the contents of the variable. This variable contains the contents of the last event raised by the serial port, which would be the entire contents of the buffer.

you need to call ClosePort once, when you no longer want the port to be connected to the Arduino.

If you can call separate subs / functions from grasshopper then you would want to call each item separately; open,  then getvalue each time you loop, and then close when you are done.

If you can't call multiple subs, then I would change the runscript sub to use an integer for the passed value instead of the boolean and work the code like this:

Code: [Select]


Sub RunScript(ByVal StepNumber as Integer)
   select case StepNumber
     case 0
       OpenPort
     case 1
       print storedvalue
     case 2
       ClosePort
   end select
 End Sub



With this you would call RunScript 0 once early in the app, and then call RunScript 1 each time you wanted to get the info from the serial port, then call RunScript 2 when you wanted to close the port.

HTH,
Rob
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 28, 2009, 05:04 am
I know we're working with baby steps... but I really do appreciate your help... and I did make progress (although not quite there). So, I followed your advice and moved the "Call Open Port" command to outside the If/Else statement that activates the loop.  So, when I tested it (by changing the boolean toggle to true... I immediately get a pop-up error message (which seems different than my normal error messages) that just says "No Port" (see image below).  However, as soon as I click ok on that message, sure enough... I get a print out of the current stored value coming in over the Serial Port.  In this example, I just have a pot sending data to pin 0 so when I turn the knob I get a value somewhere between 0-1024... and sure enough the read out said 593... I had the knob turned to the middle.  Now, the problem is that it isn't streaming... it only gives me that one number.  If I turn the boolean value back to false, I get the same "No Port" message.  I've also noticed a weird phenomenon in that that if I flip it back to true... occasionally it gives me a different read out (assuming I turned the knob on the pot)... but after 2 or 3 times of changing the boolean toggle... it just continues to give me the same value... regardless of whether or not I turn the knob on the pot.  So, we're a lot closer than we have been... but it's still getting stuck somewhere.  If you have any suggestions... I would greatly appreciate it.  Here's the code (and I'll post the images and the URL's to the images below that)  Let me know if you can't see them.
Code: [Select]
 Sub RunScript(ByVal activate As Boolean)
   Call OpenPort()
   If activate Then
     
     Call GetValue()
     print(StoredValue)

   Else
     Call ClosePort()
     print("not receiving data")
   End If
 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


(http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino_VB04.jpg)
(http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino_VB05.jpg)


http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino_VB04.jpg
http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino_VB05.jpg
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 28, 2009, 02:08 pm
Just to clarify, my advice was to remove the if / else logic completely.

You will have a couple of issues with using the boolean value the way you have it. Basically you are trying to open the port / close the port multiple times. Every time you flip the bool to false, the port is closed an no more data is going to come in. Every time you flip it to true, you are trying to reopen the port. If it is already set to true then you are tring to reopen the already open port every time you call the loop.

If at all possible you need to only ever call OpenPort and ClosePort once.

The other thing to be aware of is that with a 1 second or even half second loop on the value change, you will have to move the pot a little and wait for the call to the port. I have built the basic circuit, and run my code sample from VB to test, and it works, but polling it at a half second makes in not so instant.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 28, 2009, 06:35 pm
Ok... Something about what you last said hit me in the face (I know you've said it several times... but I guess repetition is the mother of all teachers)  You said that I should call the Open Port function only ONCE.  Which makes complete sense.  But, I was wrapping everything into one VB script component and then connecting a Timer component to that... So, every time the Timer component rebuilt the solution of anything that was connected to it (in this case the VB component that Opened the port, returned the current value, and then closed the port)... it was doing that over and over again...  I think my error has been that everything has been wrapped into only one VB component; when it should have been two.
So, what I've done now is to create two VB components... The first one is the Open/Close component which will open or close the port based on a boolean toggle.  This way, you can open up the Grasshopper definition and change the boolean toggle to True and the port will open only once... Note, I'm not connecting a Timer component to this VB script so it only updates when I change the boolean toggle.  Then, I create another VB component called Get Value... which does just that... But this time, I connect the Timer Component to this one so the Get Value function gets called everytime the Timer Component tells it to rebuild the solution... Does that logic make sense?  
So here's the thing... I'm no longer getting the "No Port" error message... but, I'm also not getting any value being returned out of the Get Value component. I've tried several different methods... I've tried passing the boolean value to the Get Value component and alling the function only when it's true (see image below)... and I've also just tried to have the 2 individual script components without any connections between the two... but I'm still not getting any values returned in the out box.  The code for both components is below and if you can see any errors in my logic or in the code, please let me know.  Thanks again.

Open/Close Component

Code: [Select]
 Sub RunScript(ByVal activate As Boolean)
   
   If activate Then
     Call OpenPort()
   Else
     Call ClosePort()
   End If
   A = activate
 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

 '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


Get Value Component

Code: [Select]
 Sub RunScript(ByVal activate As Boolean)
   
   If activate Then
     Call GetValue()
     print(StoredValue)
   Else
     print("not receiving data")
   End If
 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


 '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
#End Region


http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino_VB07.jpg
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 28, 2009, 06:58 pm
Andy,  

You have the right idea now, but I think that the problem is that if you are using two components, thus creating two instances of the same class. Each instance has no way of talking to each other, or using the values that exist in each other. In other words, you are creating instance A and opening the port, but then checking the variable in instance B which doesn't actually have the port opened.

I think that the basic logic behind your boolean switch approach was getting close, but I think you needed to use a select case method so that you have 3 choices instead of two. With the boolean you only have a true / false, when you need 3 choices so that you can open / listen / close.

I would create an integer instead of the boolean and then call the script from the timer. Set the int to 0 (open), then set it to 1 (listen) for the duration of the app, and then set it to 2 (close) when you want to close the port.

use the code below in the script for the call.

Code: [Select]
Sub RunScript(ByVal StepNumber as Integer)
   select case StepNumber
     case 0
       OpenPort
     case 1
       print storedvalue
     case 2
       ClosePort
   end select
 End Sub



I had a nice reply to the first post that you made, but hit close on accident and lost it. One thing that I wanted to bring up based on that post that you have mentioned here again. Does the timer rebuild the solution at each tick? If so none of this will work, since it is completely dependent upon the class being compiled, and an instance of the object continuing to exist for the duration of the apps life, or at least as long as you want the port to be open.

Rob
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 28, 2009, 07:17 pm
Hi Rob... Timers are new to the latest version of Grasshopper... so to be honest... I'm a little new them as well.  Here is a what is found under the help menu about the Timer Component:

Timers are object which fire update events at specified intervals. This process is reasonably dangerous since updates might occur when you do not expect them, so please be careful when using them, and only use a timer when you have no other option.

By default a new timer is not enabled. You can enable it by double clicking on the object or via the context menu. However, enabling a timer is no guarantee that it will fire update events.

First, there is a global Timer Abort which has the power to disable all timers in Grasshopper. Whenever a timer is enabled for the first time, the Global Abort will appear in the Windows notification bar. When the icon is green, it means the Global Abort is off and timers are allowed to fire events. When the icon is red, all timers are blocked. Blocked timers are displayed with a red icon instead of the timer icon on the canvas. Double clicking on the notification icon on the windows taskbar will toggle the Global Abort state.

Secondly, timers only fire events when they can make a difference. Before a timer will tell Grasshopper to recompute the solution it will erase certain objects. These are called the target of the timer object. You can add a target to a timer by click+dragging from the arrow area to the right of the timer. Drag the wire onto another object, and it will be added to the target list. You can remove objects from the target list by tracing over an existing target wire while holding the Control key.

So... I think I had an error in my last image that I sent because my timer component was drawn in Red... and not Green.  I don't think my Timer component was actually firing events... I think I can figure that part out though by changing the Global Abort method.  
Secondly, you mentioned that I should not use a boolean toggle, but instead use a Case method, where case 0 calls the Open Port function, 1 calls the Get Value, and 2 closes the port... But, were you saying that code should all go into 1 VB component (instead of 2 like my last example) where I feed an integer variable called StepNumber into the Script... and depending on that number it will call one of the different functions.  Would you then connect the timer component to that one component?  
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 28, 2009, 07:54 pm
Andy,

Quote
.. were you saying that code should all go into 1 VB component (instead of 2 like my last example) where I feed an integer variable called StepNumber into the Script... and depending on that number it will call one of the different functions.  Would you then connect the timer component to that one component?  

Exactly. Use one script, and pass the int through the timer.

I do have a concern with the following:

Quote

Before a timer will tell Grasshopper to recompute the solution it will erase certain objects. These are called the target of the timer object.


It is quite possible that the timer will erase the script object and create a new instance, which would cause the port object to vanish / close, but I'm not sure. You'll just have to try my previous suggestion an see what happens.

-Rob
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 28, 2009, 08:17 pm
Hi Robert... I'm bit confused when you said "Pass the int through the timer"... I think the int has to be passed to the VB script... and the Timer just rebuilds the solution at a given time interval.  (See the images below)  In the first image, I've just created 3 integer components which essentially just hold an individual number.  By connecting different integer components to the VB component... I will call a different case and thus a different function.  The second image shows the exact same step, only this time I'm using a slider which can be changed from it's lower limit of 0 to it's upper limit of 2.  They should basically do the same thing.  
But, I do agree with you that there does seem to be an issue with the Timer component erasing the script object (I wont be able to test the script til I get home tonight).  I'm not really sure what to do about that either.  There was a pre-cursor to the Timer component... basically one of the Grasshopper forum members (Giulio Piacentino) wrote this C# component that does more or less what the new Timer component that is built into Grasshopper does.  Here's a link to the component (It's called The Engine): http://www.giuliopiacentino.com/grasshopper-tools/.  This code is written in C# but it is posted below.  It's possible that this script does not erase the instance of the port.  I will try out both and hopefully be able to provide some feedback.
Code: [Select]
 bool RunScript(bool isOn, int speed, int resetAt)
 {

   //TheGHEngine
   //Written by Giulio Piacentino
   //Version 2009 06 06
   //more tools at www.giuliopiacentino.com/grasshopper-tools/
   
   //Note: a static constructor is defined in the additional section below
   
   //Set interval
   if(Script_Runtime_Instance.timer.Interval
     //"Script_Runtime_Instance" is the real name of "Grasshopper_Custom_Script"
   != speed){
     Script_Runtime_Instance.timer.Interval = (speed == 0 ? 1000 : speed);
   }
   
   //Set static variables
   Script_Runtime_Instance.eventOn = isOn;
   
   //Start timer
   if(isOn){
     Script_Runtime_Instance.timer.Start();
   } else {
     Script_Runtime_Instance.timer.Stop();
   }
   
   //Reset
   if(resetAt != 0 && resetAge != resetAt){
     resetAge = resetAt;
   }
   
   //Return value
   C = Script_Runtime_Instance.frameCount;
   
 }

#region "Additional methods and Type declarations"

 static public System.Windows.Forms.Timer timer;
 static public int frameCount = 0;
 static public bool eventOn = true;
 static public int resetAge = int.MaxValue;
 
 //Static constructor
 static Script_Runtime_Instance(){
   
   //Create the timer on this static thread (other types of timers won't work)
   timer = new System.Windows.Forms.Timer();
   
   //Assign the delegate only once
   timer.Tick += new EventHandler(CheckTime);
   
 }
 
 static private void CheckTime(Object myObject, EventArgs myEventArgs){
   if(IsSolutionRunning()){
     if(eventOn){
       //System.Windows.Forms.MessageBox.Show("Not updated in time");
     } else {
       System.Windows.Forms.MessageBox.Show("Checktime has been disposed even if the solution was running");
       timer.Stop();
     }
   } else {
     if(eventOn){
       //System.Windows.Forms.MessageBox.Show("Checktime's doing great");
       if(resetAge == frameCount)
         frameCount = -1;
       frameCount++;
       RedefineSolution();
     } else {
       //System.Windows.Forms.MessageBox.Show("Checktime will be stopped because it's bedtime");
       timer.Stop();
     }
   }
 }
 
 static public bool IsSolutionRunning(){
   return Grasshopper.Plugin.EH_InstanceServer.ActiveDefinition.SolutionState
     == Grasshopper.Kernel.EH_ProcessStep.Process;
 }
 
 static public void RedefineSolution(){
   Grasshopper.Plugin.EH_InstanceServer.ActiveDefinition.NewSolution(true, false);
 }

 //
 
#endregion
}


http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino_VB08.jpg
http://www.liftarchitects.com/storage/research/Grasshopper%20to%20Arduino_VB09.jpg
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 28, 2009, 08:27 pm
Sorry for the confusion. What you have is exactly what I would do. Of course it all depends upon the object staying alive, and what they mean by rebuilding the object.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 29, 2009, 03:32 am
Ok, I've tried a lot of different methods, where I call the different case numbers (putting all of the functions into 1 VB script)... and I've tried connecting both the Timer Component or the Engine component to see if I can get it to auto update... but still nothing... I did notice something though.  I remember mentioning that I was getting an error message that just said "No Port"... and I found it odd because it seemed different than the other error messages that I have gotten in the past.  BTW, I'm still getting these messages too (even with the latest code tests)... and it happens every time I take the slider to 0 (in which case I call the Open Port function).  In looking at the code, I see you placed the following code:
Code: [Select]
Catch ex As Exception
     MessageBox.Show("no port")
   End Try

It seems like this is why the error message is popping up and I was wondering if I'm ever really evening establishing a connection to the COM port.  Do you know why this error message would be happening (it happens even if the Timer component is deleted from the definition, so I think it's a completely separate issue).  Would you mind explaining the Open Port function a little bit... Man, this has to be the longest running problem thread on the forum... I appreciate all of you willingness to help though. (I have learned a lot).
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 29, 2009, 03:48 am
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.

Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 29, 2009, 04:04 am
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.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Aug 31, 2009, 04:08 pm
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.
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Aug 31, 2009, 11:59 pm
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
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Sep 01, 2009, 12:07 am
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.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Sep 01, 2009, 04:07 am
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
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Sep 01, 2009, 04:21 am
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


Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Sep 01, 2009, 05:15 am
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?  
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Sep 01, 2009, 05:23 am
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?
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Sep 01, 2009, 05:29 am
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).  
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Sep 01, 2009, 05:33 am
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.

Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Sep 01, 2009, 05:35 am
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.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Sep 01, 2009, 05:56 am
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.
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Sep 01, 2009, 06:10 am
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.
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Sep 01, 2009, 06:32 am
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
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Sep 01, 2009, 02:05 pm
Andy,

Glad to hear that you got it working!

And no problem with the help.

Rob
Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Sep 02, 2009, 07:39 am
Hi Rob,
I had one follow up question I was hoping you might be able to help me with.  I am trying to bring in the accelerometer data from a wii remote into Grasshopper.  I've got 2 lines in my Arduino code that prints 2 different lines, each with a number for the two directions of the accelerometer.
Code: [Select]
Serial.println((byte)accx,DEC);
Serial.println((byte)accy,DEC);

So, when I turn on the Serial Monitor... I get two numbers, each on their own line (which makes sense, because I'm using the println command)... However, when I read this data into Grasshopper... it combines both numbers into 1 larger number... so if my accx number = 172 and my accy number = 105, then the number that comes into Grasshopper is 172105.  If they came into Grasshopper on 2 consecutive lines then I can split the list into 2 and isolate each number... but it's more difficult when the number is combined into one 6 digit number... Is there anything in the code that would be combining the 2 numbers into 1 line?  Here is the code that we came up with.
Code: [Select]
 Sub RunScript(ByVal StepNumber As Integer)
 
   Select Case StepNumber
     Case 0
       OpenPort()
       Print("Port has been opened")
     Case 1
       'Print(StoredValue)
       Print(CInt(Val(StoredValue)))
     'A = 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")
     MessageBox.show(ex.tostring)
   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
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Sep 02, 2009, 01:21 pm
I hadn't considered that, but the point where we convert the string to an integer, it is removing all of the formatting elements of the string, and merging the numbers together.

If you remove the "cint(val(" and the "))" from the print statement you should get a string back.

Title: Re: Sending Data to Serial Port through VB
Post by: andyopayne on Sep 02, 2009, 03:53 pm
The problem is that I need the numbers to be numbers and not strings... is there any way to keep then still numbers but keep their formatting?
Title: Re: Sending Data to Serial Port through VB
Post by: hdsrob on Sep 02, 2009, 04:14 pm
Not the way we are doing it now. If I had an accelerometer I'm sure I could read the byte array of the serial buffer and do a much better job of dealing with the incoming data directly. This way you could split the x and y readings when they come in at the port, and put them into two separate variables. Then you could print them both in runScript.

I honestly think that you are at the point where it will be much easier to get that part of the code working in VB, and then copy and paste it back into the script.

I would be happy to provide some more support via email if you wanted to take that direction.

Rob