Trying to Control a CR Servo motor with Arduino. Need help!

Hello guys,

I got a project i have to control a CR Servo motor. The thing is this, the Servo Motor will be attached to a Worm Gear, and the Worm Gear will lead a Gear with 75 teeth.

So for a full rotation of the 75t Gear, i need 75 rotations for my Worm Gear (Cause it counts as 1 tooth). So here is the Question :

"How can i tell my CR Servo to rotate 75 (or a variety from 1-75) times and then stop with accuracy?"

I have been searching for days and didn't find anything similar.

What i have done until now are the following :

1) I Calibrated it to stop rotating at a 1500μs pulse.
2) I tried to use the delay() function, so i calculated how much time it would take it to make a full rotation, but still i have some deviations. I.E. Instead of rotating 19 times it was rotating 19 and 3/4 or so.
3) My circuit is pretty simple, Breadboard, Arduino, CR Servo from Parallax, Adaptor for a 5V Power Supply, Common GND Supply-Arduino-Servo.

Would really like some help here.

Thanks for your time in advance.

Get rid of the the servo and use a motor with a rotary encoder. Damaged servos such as "CR" servos have no control over position.

Mark

"How can i tell my CR Servo to rotate 75 (or a variety from 1-75) times and then stop with accuracy?"

You can't as it stands. You need a method of knowing how far the (not actually a) servo has rotated. For this you could use a rotary encoder or some other method of reliably detecting and measuring the rotation of the servo or the shaft that it is driving.

I see...

Even though i get a rotary encoder, is it compatible with CR Servos? I mean i tried the knob example Arduino has itself, and the only thing it was doing it was rotating faster or slower both directions or stop, depending my pot's position.

Wont the encoder act the same?

Thanks again, for your replies.

Wont the encoder act the same?

No. You will set the servo speed and direction in the program and use the encoder output to determine how much the servo has rotated. When it has rotated the required mount you stop the servo.

If you want to you could even do things like slowing the servo as it approaches the angle that you are aiming for.

If you want to you could even do things like slowing the servo as it approaches the angle that you are aiming for.

However, the "stopping accurately" part will be up to you. It will not be easy. \

A mechanical stop on whatever you are rotating will be better for getting it to stop accurately.

UKHeliBob:
No. You will set the servo speed and direction in the program and use the encoder output to determine how much the servo has rotated. When it has rotated the required mount you stop the servo.

Can you give me an example please of the code? How should i express that?

I managed to read my Encoder, but still can't make the connection (in code) with the servo. I mean the only thing i can think is to map the encoder's output and servo.write it but it will do the same thing as with the Knob. So i would like some little more guidance here so i can built the rest of the code.

Thanks again. :slight_smile:

Start the servo moving in the right direction at the desired speed using servo.write()
Count the encoder pulses
When you have received the correct number of encoder pulses, stop the servo.

How can i tell my CR Servo to rotate 75 (or a variety from 1-75) times and then stop with accuracy?"

How many encoder counts per encoder rotation? Multiply that by 75 and that would be the number of counts at which you stop the servo.

Hey guys, i tried to do what you said,

this is my code

[

/* interrupt routine for Rotary Encoders
 
 
 The average rotary encoder has three pins, seen from front: A C B
 Clockwise rotation A(on)->B(on)->A(off)->B(off)
 CounterCW rotation B(on)->A(on)->B(off)->A(off)
 
 
 */

#include <Servo.h>

Servo myservo;
int val;
enum PinAssignments {
  encoderPinA = 2,   
  encoderPinB = 3,   
};

volatile unsigned int encoderPos = 0;  // a counter for the dial
unsigned int lastReportedPos = 1;   // change management
static boolean rotating=false;      // debounce management

// interrupt service routine vars
boolean A_set = false;              
boolean B_set = false;


void setup() {

  myservo.attach(9);
  myservo.writeMicroseconds(1500);

  pinMode(encoderPinA, INPUT_PULLUP); 
  pinMode(encoderPinB, INPUT_PULLUP); 


  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
  // encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);

  Serial.begin(57600);  // output
}

// main loop, work is done by interrupt service routines, this one only prints stuff
void loop() { 
  rotating = true;  // reset the debouncer

  if (lastReportedPos != encoderPos) {
    Serial.print("Index:");
    Serial.println(encoderPos, DEC);
    lastReportedPos = encoderPos;
    if (encoderPos != lastReportedPos ){
      if (encoderPos > lastReportedPos ){
        myservo.write(180);
        delay(20);
        myservo.writeMicroseconds(1500);
      }
      else if (encoderPos < lastReportedPos ){
        myservo.write(0);
        delay(20);
        myservo.writeMicroseconds(1500);
      }
      else{
        myservo.writeMicroseconds(1500);
        delay(20);
      }
    }
    else{
      myservo.writeMicroseconds(1500);
      delay(20);
    }
  }
}

// Interrupt on A changing state
void doEncoderA(){
  // debounce
  if ( rotating ) delay (1);  // wait a little until the bouncing is done

  // Test transition, did things really change? 
  if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
    A_set = !A_set;

    // adjust counter + if A leads B
    if ( A_set && !B_set ) 
      encoderPos += 1;

    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB(){
  if ( rotating ) delay (1);
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set ) 
      encoderPos -= 1;

    rotating = false;
  }
}

]

My Continuous Rotation servo in this case, does 1 lets say mini step every a few clicks like 10-12 of the Rotary Encoder’s, but in the same direction CounterClockwise.
Still it doesn’t work like i want and i think i didn’t do exact what you said, so would like some help with this, if you have time.

The code for getting the Encoder’s pulses is from Arduino’s website and i changed a few things to work for me. And it’s working fine, with just a 10% of debounce problem.

Here are some photos of the connections, maybe i got something wrong there too.



Thanks for the help.

Why have you made things so complicated ?

Apart from anything else you have this in your ISRs

 if ( rotating ) delay (1);  // wait a little until the bouncing is done

delay() depends on interrupts
Interrupts are automatically disabled in ISRs
Do you see a potential problem ?

I have no time to look further at the moment but will look later.

// main loop, work is done by interrupt service routines, this one only prints stuff

Absolute nonsense.

Do you really need to do anything in the ISR except increment a counter and why have 2 ISRs ? Does it actually matter which way the servo is turning ? If you start the counter from zero when the motor starts then counting up is good for either direction.

You seem to have taken most/all of the code from an example for a manual rotary encoder which might be turned either way at any time. This is not the case with a servo because you tell it which way to move so you don’t need to detect it.

Hi, i took the code from arduino's website. http://playground.arduino.cc/Main/RotaryEncoders

The only things i changed was the commands for the servo.

The only thing i want to do, is to tell my servo this : you are now in 0-start position, turn 360 degrees 20 times clockwise. Stop. And turn 360 degrees 20 times counterclockwise. That's all i'm trying to do.

So i thought that i would need the rotary encoder to detect its position and if it's not in start-0 position, then i tell it to go there, and from there it will start rotating 20 times, or 30 or so on...

This is my 1st project on Arduino, so i dont know its language so good. Plus i have never used a rotary encoder so i don't know again how to count pulses or to combine it with a servo.

The only thing i want to do, is to tell my servo this : you are now in 0-start position, turn 360 degrees 20 times clockwise. Stop. And turn 360 degrees 20 times counterclockwise. That's all i'm trying to do.

You can't tell the servo that. It's brain was removed. All you can do is tell it to start moving at some speed and to start moving at another speed. If the appropriate second speed is chosen, the motor will stop.

It is up to you, then, to write the code to read the encoder, and decide when the motor has accomplished the goal of rotating the encoder through 20 complete turns.

so i don't know again how to count pulses

Well, the code on the site you linked to does.

or to combine it with a servo.

Suppose that the goal was, instead, to simply turn an LED on or off. Could you manage that? Because that is ALL you can do with the not-a-servo that you have. You can turn it on or you can turn it off.

You can turn it on or you can turn it off.

Well, you can give the continuous rotation servo commands that will result in it stopping, rotating in two different directions, and at what speed to rotate. Seems to be more than just LED on/off.

Seems to be more than just LED on/off.

Not really. There is a time to turn the servo on (at some speed) and there is a time to turn it off. For what OP is doing - winding up or down a string for a Halloween prop - speed doesn't really matter.

Obviously, getting the device to wind the string up or down for some critical function, one would adjust the speed of the motor downwards as the end approached.

But, OP hasn't even gotten to the point where he/she can start the motor which starts the encoder pulsing, and hasn't been able to read the encoder, and has no idea when to stop the motor, so adding speed adjustments into the mix isn't going to happen yet.

Not really. There is a time to turn the servo on (at some speed) and there is a time to turn it off. For what OP is doing - winding up or down a string for a Halloween prop - speed doesn't really matter.

Reality check, the "winding up or down" usually involves bidirectional rotation.

Reality check, the "winding up or down" usually involves bidirectional rotation.

Which for a continuous rotation servo means what? Yeah, a different "speed".

Hi guys,

Hey Paul thanks for your comments, you really helped me to get the idea of what to do and how.

I managed to control my servo through the Rotary Encoder. Now every time i do one click clockwise on my rotary my servo moves just a bit, and if i do one click counterclockwise its moves the same velocity in the opposite direction. I saw that i can control the velocity through the delay in my program.

 /* interrupt routine for Rotary Encoders
 
 
 The average rotary encoder has three pins, seen from front: A C B
 Clockwise rotation A(on)->B(on)->A(off)->B(off)
 CounterCW rotation B(on)->A(on)->B(off)->A(off)
 
 
 */

#include <Servo.h>

Servo myservo;
int val;
enum PinAssignments {
  encoderPinA = 2,   
  encoderPinB = 3,   
};

volatile unsigned int encoderPos = 0;  // a counter for the dial
unsigned int lastReportedPos = 1;   // change management
static boolean rotating=false;      // debounce management

// interrupt service routine vars
boolean A_set = false;              
boolean B_set = false;


void setup() {

  myservo.attach(10); //Servo attached in pin 10

  pinMode(encoderPinA, INPUT_PULLUP); 
  pinMode(encoderPinB, INPUT_PULLUP); 
  pinMode(9, OUTPUT); //LED at pin 9 for light Indicator of rotarys Change

  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
  // encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);

  Serial.begin(57600);  // output
}


void loop() { 
  rotating = true;  // reset the debouncer
  digitalWrite(9, LOW); //LED OFF
  myservo.writeMicroseconds(1500); // Servo OFF

  if (lastReportedPos != encoderPos) {
    Serial.print("Index:");
    Serial.println(encoderPos, DEC);
    digitalWrite(9, HIGH); // LED lights in every count-click of the rotaty
    delay(20);
    if (encoderPos>lastReportedPos)
    {
      myservo.write(50); //Servo ON Clockwise NOT full speed
      delay(100);
    }
    else
    {
      myservo.write(150); // Servo ON CounterClockwise NOT full speed
      delay(100);
    }
    lastReportedPos = encoderPos;
  }
}



// Interrupt on A changing state
void doEncoderA(){
  // debounce
  if ( rotating ) delayMicroseconds (1000);  // wait a little until the bouncing is done

  // Test transition, did things really change? 
  if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
    A_set = !A_set;

    // adjust counter + if A leads B
    if ( A_set && !B_set ) 
      encoderPos += 1;

    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB(){
  if ( rotating ) delayMicroseconds (1000);
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set ) 
      encoderPos -= 1;

    rotating = false;
  }
}

Now i am going to study and think how i can make it rotate one full cycle 360 degrees through the program, and how i can use the pulses from the Rotary as position tracking or something. And how to vanish that little bounce i still got at all.

If you have any idea, or something like the LED example, i would really appreciate it.

Zoomkat sorry man, but the thing with the LED really helped me on getting into the logic for doing this.

Thanks PaulS,

Thank you guys. :slight_smile: