Problem with Servo and Encoder

Hello Friends,

I am trying to rotate one servo motor as per rotary encoder’s rotation.

But the problem is, when encoder changes its rotation direction, servo motor doesn’t change rotation direction for minimum, 180 degrees of rotation of encoder.

I have tested the Servo motor, by running “sweep” code from example, and it was running just fine.
I also checked Encoder by running separate encoder code, and it is also running fine.

Even in the code i am using, in serial monitor the value of angle is updated when encoder changes direction, but the motor doesn’t change direction with it.

Please help me.

Servo_encoder_uniform_code.ino (2.48 KB)

One basic thing. The Servo library does not control servo position to 4 decimal places. Servo.write(angle) takes an integer for the angle. Trying to use a float with it and subtracting 0.5 is very often not going to do what you're probably expecting.

Steve

Thanks for your reply.

Actually, for 540 degrees of rotation of encoder in any one direction, my servo should rotate 30 degrees, in that direction. That's why i have used float. But after your advice, I changed increment value to +1, instead of 0.5, still there's no improvement in output.

I did some more testing with code, and with use of Serial print i got some more information about the problem.

If my encoder is rotating in clockwise direction, motor will roate clockwise properly, now if i change the roation direction of encoder to Anticlockwise, then servo will not move at all, and it will start to rotate anticlockwise, after encoder had made 180 degrees rotation, in Anticlockwise direction, same for if encoder change direction from Anyiclockwise to clockwise. And the according to serial monitor, even though servo motor was not moving, it was receving servo.write instruction.

So i am really confused, if that has to do something with servo motor. But the same motor works just fine in "Sweep" code, given in Arduino example codes.

I even changed the servo motor, but the problem wasn still not solved.

Any ideas anyone, what i could be doing wrong?

Any help appericated. Thanks.

ABP

Post your code inside code tags, not as an attached file. I don't know about others, but I read these forums on a tablet or on a phone and opening a .ino file is a pain. Won't happen.

Thanks for taking interest.

Here is my:

#include <Servo.h>
#define outputA 2
 #define outputB 3
 int counter = 0; 
 int aState;
 int aLastState;  
 int servoPin = 9;
 
Servo servo;  
 
float angle = 90.00;   // set initial servo position in degrees 





void setup() {
  // put your setup code here, to run once:

pinMode (outputA,INPUT);
   pinMode (outputB,INPUT);
   
   Serial.begin (9600);
   // Reads the initial state of the outputA
   aLastState = digitalRead(outputA); 

   servo.attach(servoPin);
   servo.write(angle); // set intital position of servo at 90 degree 
}


void loop() {
  // put your main code here, to run repeatedly:

   aState = digitalRead(outputA); // Reads the "current" state of the outputA
   // If the previous and the current state of the outputA are different, that means a Pulse has occured
   if (aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(outputB) != aState) { 
       counter ++;
      
       LeftTurn(counter);
     } else {
       counter --;
       
       RightTurn(counter);
     }
   // Serial.print("Counter: ");
   //Serial.println(counter);
    // Serial.print("Angle: ");
    // Serial.println(angle);
    
    
   } 
   aLastState = aState; // Updates the previous state of the outputA with the current state

           

}


void LeftTurn(int c) {

 if ((-70<=c) && (c<=70)){
          angle=angle+0.5;
          servo.write(angle);
          
          Serial.println("      Left Turn     ");
          Serial.print("Position:");
          Serial.println(c);
    
          Serial.print("Angle: ");
          Serial.println(angle);
          delay(50);
    
 }

}

void RightTurn(int c) {

 if ((-70<=c) && (c<=70)){
        angle=angle-0.5;
        servo.write(angle);
        
        Serial.println("     Right Turn     ");
        Serial.print("Position:");
        Serial.println(c);
    
        Serial.print("Angle: ");
        Serial.println(angle);
        delay(50);
    
 }

}
    delay(50);

Not a good idea. This will cause it to miss encoder steps.

Ohh! Thanks for pointing.

I changed the delay, to 10, but the problem is still there!

I don’t know what could be wrong, now?

Here’s the code with delay set to 10:

#include <Servo.h>
#define outputA 2
 #define outputB 3
 int counter = 0; 
 int aState;
 int aLastState;  
 int servoPin = 9;
 
Servo servo;  
 
float angle = 90.00;   // set initial servo position in degrees 





void setup() {
  // put your setup code here, to run once:

pinMode (outputA,INPUT);
   pinMode (outputB,INPUT);
   
   Serial.begin (9600);
   // Reads the initial state of the outputA
   aLastState = digitalRead(outputA); 

   servo.attach(servoPin);
   servo.write(angle); // set intital position of servo at 90 degree 
}


void loop() {
  // put your main code here, to run repeatedly:

   aState = digitalRead(outputA); // Reads the "current" state of the outputA
   // If the previous and the current state of the outputA are different, that means a Pulse has occured
   if (aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(outputB) != aState) { 
       counter ++;
      
       LeftTurn(counter);
     } else {
       counter --;
       
       RightTurn(counter);
     }
   // Serial.print("Counter: ");
   //Serial.println(counter);
    // Serial.print("Angle: ");
    // Serial.println(angle);
    
    
   } 
   aLastState = aState; // Updates the previous state of the outputA with the current state

           

}


void LeftTurn(int c) {

 if ((-70<=c) && (c<=70)){
          angle=angle+0.5;
          servo.write(angle);
          
          Serial.println("      Left Turn     ");
          Serial.print("Position:");
          Serial.println(c);
    
          Serial.print("Angle: ");
          Serial.println(angle);
          delay(10);
    
 }

}

void RightTurn(int c) {

 if ((-70<=c) && (c<=70)){
        angle=angle-0.5;
        servo.write(angle);
        
        Serial.println("     Right Turn     ");
        Serial.print("Position:");
        Serial.println(c);
    
        Serial.print("Angle: ");
        Serial.println(angle);
        delay(10);
    
 }

}

Abp: I changed the delay, to 10, but the problem is still there!

How much time will the printing of 38 or so characters at a (slow) speed of 9600 baud take...

I am sorry, i didn't understand your question.

If you're asking, that if there's any lag in Serial Monitor, then the answer is no. The serial monitor, updates, angle values in almost real time.

And the problem i am facing is, the motor doesn't respond to change of direction.

When i start the code for the first time, and rotate the encoder in anyone direction, let's say, anticlockwise, then the servo motor, will rotate without any problem in anticlockwise direction, but after some rotation in anticlockwise direction, if i changed the rotation direction of encoder to, clockwise, the servo will stop, and won't change its direction until encoder has made, approximately 180 degrees of rotation. After motor has started rotating in clockwise direction, and i change the rotation direction of encoder again to Anticlockwise, the motor will again stop moving and won't change direction for next 180 degrees of encoder rotation.

I think motor is stopping whenever the function changes from LeftTurn() to RightTurn() and vice-versa. But confusingly, Serial Monitor says that correct function is being executed.

Thanks for you reply.

ABP

Maybe it’s just me, but it’s disturbing that you name an input pin outputA and outputB. I know it’s the output on the encoder side, but it’s an input on the Arduino (and code) side. But that’s nit picking.

I think your main problem is in delays, whether they are caused by delay() or by the serial output. Encoders are working probably best in interrupt routines. During the delays, the state of the encoder pins might change.

void LeftTurn(int c) {

if ((-70<=c) && (c<=70)){
         angle=angle+0.5;
         servo.write(angle);

What does 70 mean here?

I note that you're still using floats and increments of 0.5. I guess I'm wasting my time here.

One more try...since you have all those time-wasting Serial prints in there please post the results that you see when the servo is responding correctly and then when it is not doing what you want.

Steve

Actually, for 540 degrees of rotation of encoder in any one direction, my servo should rotate 30 degrees, in that direction. That's why i have used float.

In other words, when the encoder is twisted, the servo rotates. For 18 degrees rotation of the encoder, the servo rotates 1 degree. I'd suggest you simplify the whole idea to this: Keep track of the absolute position of the rotary encoder. You can count steps. 540 degrees is 1½ revolutions, right? If one revolution is 24 steps, you have 36 steps for your 30 degrees of servo movement. Have a routine (an interrupt routine would be the best) count the steps your rotary encoder does. You will have an "absolute" position of 9 revolutions corresponding to an "absolute" position of 180 degrees of the servo. 9 revolutions times 24 steps per revolution is 216 steps. Your rotary encoder routine should count from 0 to 215 when rotating. Have the counter stop at 215, even though the rotation continues. Likewise when rotating in the other direction, have it stop at 0. Then map the 0 - 215 range to 0 - 180 degrees, which will be your command to the servo.

There's no need for separate TurnLeft() and TurnRight() routines. Just get the rotary encoder position, map it to an angle between 0 and 180 and write to servo.

Any time I am working with encoders, I use Paul Stoffregen's encoder library. This is usually the first one you find if you search for an 'official' Arduino library.