Go Down

Topic: Control servo with Visual Basic 2008 (Read 4263 times) previous topic - next topic

Deco Aoreste

Hello,

I want to control a simple servo with Visual Basic 2008. I want to use a trackbar with 180 points. The servo is attached to digital pin 9. I tried this 2 simple codes:

Visual Basic 2008 code:
Code: [Select]
Private Sub TrackBar1_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar1.Scroll
       If RadioButton2.Checked = True Then
           SerialPort1.Write(TrackBar1.Value)
       End If
   End Sub


Arduino code:
Code: [Select]
#include <Servo.h>

Servo myservo;

int SerialValue = 0;

void setup()
{
 myservo.attach(9);
 Serial.begin(9600);
}


void loop() {
 SerialValue = Serial.read();
   myservo.write(SerialValue);
   delay(15);
 }


...But it doesn't work. The servo moves somewhat, but not to the right angle, not even close. It just moves like it's confused and doesn't know what to do.

Does anyone know a solution to this?

Many thanks,
Deco Aoreste

PaulS

In the Arduino code, loop executes in an endless loop, potentially millions of times a second. During each iteration, it will (try to) read a byte from the serial port, and send the servo there, then wait 15 milliseconds for the servo to get there.

You are not checking to see if there is serial data to read, nor are you checking that the value read is reasonable.

If there is no data to be read, Serial.read returns -255. That is probably not an angle that the servo can rotate to.

Add a call to Serial.available(), and only read from the serial port if there is data to read. Only move the servo if the value read from the port is reasonable.

Ran Talbott

Quote
But it doesn't work.


Sure it does.  It just doesn't work the way you think it does  :)

The Arduino's read() doesn't work the same way as VB's:  it only gets one character at a time.  You need to accumulate the characters into a string,  and convert that to a number with a function like http://atoi().  Or you can convert the characters "on-the-fly" by translating each digit as it comes in,  and accumulating the value.

The examples on the reference page for the Serial library don't seem to show those,  but you can find examples posted in the forums.

Deco Aoreste

Thanks for replying.

I tried the things that you said, but it still doesn't work. Please note that I'm a beginner, this is one of my first projects.

So this is my code now:
Code: [Select]
#include <Servo.h>

Servo myservo;

int SerialValue = 0;
char incomingByte;
int outputValue;

void setup()
{
 myservo.attach(9);
 Serial.begin(9600);
}


void loop() {
 if (Serial.available() > 0) {
   SerialValue = Serial.read();
   outputValue = atoi(incomingByte);
   myservo.write(outputValue);
   delay(15);
 }
}


I get an error at this line:
Code: [Select]
outputValue = atoi(incomingByte);
I get this error:

error: invalid conversion from 'char' to 'const char*

What should I do to make it work?

Thanks,
Deco Aoreste

AWOL

You're reading a single "char", and passing it to "atoi", which expects a string (char*).

However, a single decimal digit will result in only very limited movement of the servo.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Deco Aoreste

I understand. But what do I need to do to make it work? I made the char 'incomingByte' to a string(char*), and I don't get errors in the code anymore. But it still doesn't work. Does anyone have an example code? I would really appreciate it if you would share it with me.

PaulS

You'll need to modify the VB application to send more data. Currently it sends the value as a string, apparently. If the VB application sends data as the mouse moves, it might send

'1', '5', '6', 1', '5', '8', '1', '4', 9'...

In that data stream, where does one value begin and end? Is the data supposed to represent 15, 61, 58, 14, 9 or 156, 158, 149?

Change the VB app. to send '<', '1', '5', '6', '>', '<', '1', '5', '8', '>', '<', '1', '4', 9', '>'.... This way, there is no ambiguity.

Then, do something like this on the Arduino:

Code: [Select]
int servoValue = 0;
char inData[12];
int index = 0;
bool started = false;
bool ended = false;

void loop()
{
  if(Serial.available() > 0)
 {
    while(Serial.available() > 0)
    {
       char aChar = Serial.read();
       if(aChar == '<')
       {
          index = 0;
          inData[index] = '\0'; // Null out the array
          started = true;
          ended = false;
       }
       if(aChar != '>' && started && !ended)
       {
          inData[index] = aChar;
          index++;
          inData[index] = '\0';
       }
       if(aChar == '>')
       {
           ended = true;
           started = false;
       }

       if(ended && !started)
       {
           servoValue = atoi(inData);
           // Move the servo
           // Wait for it to get there
           ended = false; // Set up for the next packet
       }
    }
 }
}


This code uses two flags, started and ended, to indicate whether we have encountered a start of data marker and and end of data marker.

If the character is a start of data marker, started is set to true, and the array is initialized.

If the character is an end of data marker, started is set to false, and ended is set to true. This means that we can now use the data received, because we know it is complete.

If the character is not an end of data marker, and we have received a start of data marker, the character is added at the end of the string, the pointer is advanced, and the string is (again) properly terminated.

If an end of data marker has been received (ended is true and started is false), we move the servo, and set the flags to false.

Try out various data loss scenarios, and you'll see that a dropped start of data marker is handled properly - the next one starts the process of receiving data again. An end of data marker loss is handled the same way.

The only thing that is not handled is a dropped character between start and end of data markers. That could be handled by making the payload look like:

<numberOfcharacters:validCharacters>

An example:

<3:156>

Then, the received string should look like '3', ':', '1', '5', '6', NULL. You could then convert the value before the : to an integer, 3, and then make sure that there are 3 characters after the :. This is probably more than you really need to deal with, though, for this simple application.

Deco Aoreste

I changed your code so I thought it would work, but it didn't. This is my Arduino code now:
Code: [Select]
int select;
int val;
int val2;
int servoValue = 0;
char inData[12];
int index = 0;
bool started = false;
bool ended = false;
int pos;

#include <Servo.h>

Servo myservo;

void setup()
{
 myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}

void loop()
{
  if(Serial.available() > 0)
 {
    while(Serial.available() > 0)
    {
       char aChar = Serial.read();
       if(aChar == '<')
       {
          index = 0;
          inData[index] = '\0'; // Null out the array
          started = true;
          ended = false;
       }
       if(aChar != '>' && started && !ended)
       {
          inData[index] = aChar;
          index++;
          inData[index] = '\0';
       }
       if(aChar == '>')
       {
           ended = true;
           started = false;
       }
         if(aChar == ':')
         {
           select = pos;
           pos = 0;
           val = 1;
         }
       if(ended && !started)
       {
           servoValue = atoi(inData);
           myservo.write(pos);
           delay(15);
           ended = false; // Set up for the next packet
       }
         else{
           select = Serial.read();
           val2 = Serial.read();
           if(val = 1){
             val2 = Serial.read();
             val2 * 100;
             pos = val2;
           }
           else if(val = 2){
             val2 = Serial.read();
             val2 * 10;
             pos = pos + val2;
           }
           else if(val = 3){
             val2 = Serial.read();
             pos = pos + val2;
             val = 0;
           }
         }
    }
 }
}


And this is my Visual Basic 2008 code:
Code: [Select]
Private Sub TrackBar1_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar1.Scroll
       Dim value As Integer
       SerialPort1.Write("<")
       If TrackBar1.Value.ToString.Count = 1 Then
           SerialPort1.Write("1")
           SerialPort1.Write(":")
           SerialPort1.Write(TrackBar1.Value)
       ElseIf TrackBar1.Value.ToString.Count = 2 Then
           SerialPort1.Write("2")
           SerialPort1.Write(":")
           SerialPort1.Write(TrackBar1.Value.ToString.First)
           SerialPort1.Write(TrackBar1.Value.ToString.Last)
       ElseIf TrackBar1.Value.ToString.Count = 3 Then
           SerialPort1.Write("3")
           SerialPort1.Write(":")
           SerialPort1.Write(TrackBar1.Value.ToString.First)
           value = TrackBar1.Value - 100
           SerialPort1.Write(value - Val(TrackBar1.Value.ToString.Last))
           SerialPort1.Write(TrackBar1.Value.ToString.Last)
       End If
       SerialPort1.Write(">")
   End Sub


PaulS

Start by getting the VB application to write '<', TrackBar1.Value.ToString, and '>'. If TrackBar1's value is 159. it should write <159> to the serial port.

On the Arduino side, remove this stuff:

Code: [Select]
         if(aChar == ':')
         {
           select = pos;
           pos = 0;
           val = 1;
         }


and

Code: [Select]
         else{
           select = Serial.read();
           val2 = Serial.read();
           if(val = 1){
             val2 = Serial.read();
             val2 * 100;
             pos = val2;
           }
           else if(val = 2){
             val2 = Serial.read();
             val2 * 10;
             pos = pos + val2;
           }
           else if(val = 3){
             val2 = Serial.read();
             pos = pos + val2;
             val = 0;
           }


Deco Aoreste

I tried that earlier, but that didn't work. The servo doesn't move. These are my codes now:

Visual Basic 2008
Code: [Select]
Private Sub TrackBar1_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar1.Scroll
       SerialPort1.Write("<")
       SerialPort1.Write(TrackBar1.Value.ToString)
       SerialPort1.Write(">")
   End Sub


And this is my Arduino code:
Code: [Select]
int servoValue = 0;
char inData[12];
int index = 0;
bool started = false;
bool ended = false;

#include <Servo.h>

Servo myservo;

void setup()
{
 myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}

void loop()
{
  if(Serial.available() > 0)
 {
    while(Serial.available() > 0)
    {
       char aChar = Serial.read();
       if(aChar == '<')
       {
          index = 0;
          inData[index] = '\0'; // Null out the array
          started = true;
          ended = false;
       }
       if(aChar != '>' && started && !ended)
       {
          inData[index] = aChar;
          index++;
          inData[index] = '\0';
       }
       if(aChar == '>')
       {
           ended = true;
           started = false;
       }
       if(ended && !started)
       {
           servoValue = atoi(inData);
           myservo.write(servoValue);
           delay(15);
           ended = false; // Set up for the next packet
       }
         }
    }
 }

(Almost the same as your code, the only change is 'myservo.write(servoValue)' and 'Delay(15)').


mem

#10
Feb 28, 2010, 11:49 am Last Edit: Feb 28, 2010, 11:52 am by mem Reason: 1
you are missing the Serial.begin statement in setup - without this, Serial wont work.

here is a shorter version of your code. Its untested but it may give you some ideas on how to simplify the task of getting serial data.

Code: [Select]
#include <Servo.h>

Servo myservo;

int pos=0;

void setup()
{
 Serial.begin(9600);
 myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}

void loop()
{
 if(Serial.available() > 0)
 {
   char aChar = Serial.read();
   if(aChar == '<')
   {
     pos = 0;
     do{
         while(Serial.available() <1)
            ; // wait for a character
       aChar = Serial.read();    
       if(aChar >= '0' && aChar <= '9')              // is ch a number?  
         pos = pos * 10 + aChar - '0';           // yes, accumulate the value      
     }
     while(aChar != '>');
     myservo.write(pos);
     pos = 0;
   }
 }

Deco Aoreste

It works!
Both mem and PaulS; thank you very much!  :)

It was pretty stupid of me to forget the Serial.begin(9600); command.
I'll post the 2 codes here, just for administration, for other people who want to do this and cannot find it on the internet.

Visual Basic 2008 (my trackbar has 170 points)
Code: [Select]

   Private Sub TrackBar1_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar1.Scroll
       SerialPort1.Write("<")
       SerialPort1.Write(TrackBar1.Value.ToString)
       SerialPort1.Write(">")
   End Sub


Arduino code
Code: [Select]
#include <Servo.h>

Servo myservo;

int pos=0;

void setup()
{
 Serial.begin(9600);
 myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}

void loop()
{
 if(Serial.available() > 0)
 {
   char aChar = Serial.read();
   if(aChar == '<')
   {
     pos = 0;
     do{
         while(Serial.available() <1)
           ; // wait for a character
       aChar = Serial.read();    
       if(aChar >= '0' && aChar <= '9')              // is ch a number?  
         pos = pos * 10 + aChar - '0';           // yes, accumulate the value
     }
     while(aChar != '>');
     myservo.write(pos);
     pos = 0;
   }
 }
}


Again, thank you very much!

ChickenVisualBasic

I know a lot about Visual Basic, but doen't understand this at all...
I'm using Visual Basic 2008 Express Edition and an Arduino UNO and I want to make a GUI with 4 buttons, if you click one of them a LED on a breadboard connected to the Arduino turn On/Off.
How do i code it?

It would be great if you could post the FULL code, for at least one button...

Thank You Very Much

Graynomad

You should be able to do it using the above code as a base.

In VB just send a number (1, 2, 3, 4) according to the button pressed.

On the Arduino read that number and do a digitalWrite() to the appropriate pin.

It's actually a lot easier than the above because you only have to work with one character at a time.

______
Ron

Rob Gray aka the GRAYnomad www.robgray.com

ChickenVisualBasic

Could you give me an example code part for arduino and Visual Basic.

Would be great thanks,

Go Up