Control servo with Visual Basic 2008

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:

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:

#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

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.

But it doesn't work.

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

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 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.

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:

#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:

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

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.

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.

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:

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.

I changed your code so I thought it would work, but it didn't. This is my Arduino code now:

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:

 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

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:

          if(aChar == ':')
          {
            select = pos;
            pos = 0;
            val = 1;
          }

and

          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;
            }

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

Visual Basic 2008

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:

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)').

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.

#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; 
    }
  }

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

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)

    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

#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!

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

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

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

Would be great thanks,

If you're an experienced programmer you shouldn't need another example, the above can easily be modified.

Here's a quick and dirty example for the Arduino

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

void loop() {
  if(Serial.available() > 0)  {
    char aChar = Serial.read();
    switch (aChar) {
 
       case '1':
          digitalWrite (2, HIGH);
          break;
          
       case '2':
          digitalWrite (3, HIGH);
          break;
 
       case '3':
          digitalWrite (4, HIGH);
          break;
 
       case '4':
          digitalWrite (5, HIGH);
          break;       
    }
  }
}

That only turns the LEDs on, I'll let you figure out how to turn them off.

I know a lot about Visual Basic,

Then you shouldn't need any help with that side of things :slight_smile:


Rob

If you're a more experienced in Visual Basic, you could just upload the "standard Firmata" sketch to your Arduino then download the .DLL's to include with your projects.

One thing you need to do though, on the Visual Basic side is change the Baud Rate. (57600 instead of whatever the default is)

Other than that, it's fairly simple. You just use the same commands in Visual Basic that you would in the Arduino IDE, for example,

arduino.pinMode(SERVO_PIN, Firmata.FirmataVB.SERVO)
arduino.analogWrite(SERVO_PIN, SERVO_VALUE)

for some reason the VB side uses analogWrite, but it works for the servo as well.

I've Never Done anything with serial port in Visual Basic before, and I have my Arduino script finished,

I just need a Visual Basic Script, that Let's me send a Number from my VB program to COM14,
Could you give me an example...

What do I have to Import? Without Firmata,

Thanks for your help,

Maybe you could give me an example with Public Class and a Button Click Event to turn a LED on Pin 13 on,

Thank you,

OK, here's a modified snippet from something I just wrote. I didn't explicitly import anything so I assume the libraries are automatically included.

  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        SerialPort1.PortName = "Com14"
        SerialPort1.Open()

    End Sub


 Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
             SerialPort1.Write("3")
 End Sub

   Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
        If SerialPort1.IsOpen() Then
            SerialPort1.Close()
        End If

    End Sub

Rob