Arduino with rotary encoder - (S4 from usdigital)

Yo!

I've tried for a while to program a rotary encoder for controlling a dc motor. If I want the motor to rotate only 60 degrees, I would like my encoder to count 60 degrees and stop the motor when the motor have reach 60 degrees.

The encoder have 4 cases (00, 01, 10, 11) and two channels (a and b).

Here's an example:

//usdigital S4 optical encoder:
int a = 2;//channel a
int b = 3;//channel b

int counter = 0;
int cycle = 0;
int valA0 = 0;
int valB0 = 0;
int valAB0 = 0;
int valA = 0;
int valB = 0;

void setup()
{
      pinMode(a, INPUT);
      pinMode(b, INPUT);
}

void loop()
{
      valA = digitalRead(a);
      valB = digitalRead(b);
      while(valA0 != valA || valB0 != valB)
      { 
            int valAB = 0;
            valAB = (10*valA) + valB;
            
            switch (valAB)
            {
                  case 0:
                   if(valAB0 == 1) 
                   {
                         counter = counter-1;
                   }
                   else 
                   {
                         counter = counter + 1;
                   }
                   break;
                  
                   case 1:
                   if(valAB0 == 11) 
                   {
                         counter = counter -1;
                   }
                   else 
                   {
                         counter = counter + 1;
                   }
                   break;
                  
                  case 11:
                  if(valAB0 == 10) 
                  {
                         counter = counter -1;
                   }
                   else 
                   {
                         counter = counter + 1;
                   }
                   break;

                  case 10:
                  if(valAB0 == 0) 
                  {
                         counter = counter -1;
                   }
                   else 
                   {
                         counter = counter + 1;
                   }
                   break;                  
            }
            
            if(counter >= 4) 
            {
                  counter = 0;
                  cycle += 1;
            } 
            
            if(counter <= -4)
            {
                  counter = 0;
                  cycle = cycle - 1;                                                
            }
            
           
            if(cycle >= 360 || cycle <= -360)
            {
                   cycle = 0;     
            }
            
            delayMicroseconds(100);

            valA0 = valA;
            valB0 = valB;
            valAB0 = (10*valA0) + valB0;
            
}

I've tried to use the function "attachInterrupt()" as well but didn't work so well.
The encoder have 360 CPR (cycles per revolution) but sometimes if I turn the shaft on the encoder really fast, the arduino board have problems counting all the phases/cycles.

I don't know what's left for me to do here.
Does someone know how I can make this work properly?
Let the encoder count i.e. 60 degrees when the motor is running and then stop the motor.
I'm frustrated over that I can't find any examples on how to program a rotary optical encoder like this. There is some code on the playground but its not the same case as mine.

Is this an optical or mechanical encoder? If it is mechanical then you could be suffering from contact bounce.

but its not the same case as mine.

in what way?
That sequence :- (00, 01, 10, 11) is not what it output is it, in that order, it looks wrong.
Also this line is all wrong :-
valAB = (10*valA) + valB;
Why are you trying to combine the values? It makes no sense and slows down the program a lot. You can't afford to be slow with a rotary encoder. Just use if(valA == 1 && valB==0) ... or whatever. This is much faster than an integer multiply.

You can always resort to hardware:-
http://www.thebox.myzen.co.uk/Workshop/Rotary_Max.html

Rotary encoder info:

http://forum.sparkfun.com/viewtopic.php?p=65052

I also have a PDF doc on the subject but I do not see a way to attach it to this post. I can email it to you.

Hi again. First of all, thanks for tips & tricks.

I've work around some of the problems with programming the encoder. I now have this code:

//pins:
//usdigital S4 optical encoder:
int a = 2;//channel a
int b = 3;//channel b
//motordriver POLULU MD05A:
int IN1_Motor = 6;//direction of motor
int IN2_Motor = 7;//direction of motor
int D2_Motor = 10;//PWM for motor
int D1 = 12;//set to LOW
int EN = 13;//enable signal
//variables
const byte MESSAGE_PACKET_LENGTH = 2;//position, speed
const char POSITION_HEADER = 'P';
const char SPEED_HEADER = 'S';
int packetsRecieved = 0;
char* motor_angle = "yyy";
char* speed_value = "xxx";
int error = 0;
int position = 0;
int degree = 0;
int velocity = 0;
int cycleCounter = 0;
int EncoderAbsoluteDegree = 0;
int teller = 0;//counter for encoder
int cycle = 0;
int valA0 = 0;
int valB0 = 0;
int valAB0 = 0;
int valA = 0;
int valB = 0;
boolean MotorEnable = false;
boolean RUN = false;

void setup()
{
      pinMode(a, INPUT);
      pinMode(b, INPUT);
      pinMode(IN1_Motor, OUTPUT);
      pinMode(IN2_Motor, OUTPUT);
      pinMode(D2_Motor, OUTPUT);
      pinMode(D1, OUTPUT);
      pinMode(EN, OUTPUT);
      Serial.begin(9600);
}

void loop()
{
      serialCom();
      if(RUN) 
      {
            int newPOS = EncoderAbsoluteDegree + degree;
            if (newPOS < 361 && newPOS > -361)
            {
                  cycle = 0;
                  if (degree < 0)
                  {
                        while (cycle > degree) 
                        {
                              encoderCycle();
                              setMotor(degree,velocity);
                        }
                  } 
                  else
                  {
                         while (cycle < degree) 
                        {
                              encoderCycle();
                              setMotor(degree,velocity);
                        }
                  }
                  EncoderAbsoluteDegree = EncoderAbsoluteDegree + cycle;
                  Serial.println("EncoderAbsoluteDegree: ");
                  Serial.println(EncoderAbsoluteDegree);
            }
            else 
            { 
                  digitalWrite(IN1_Motor, LOW);
                  digitalWrite(IN2_Motor, LOW);
                  digitalWrite(EN, LOW);
                  digitalWrite(D1, HIGH);
                  Serial.println("EncoderAbsoluteDegree: ");
                  Serial.println(EncoderAbsoluteDegree);
             }
            digitalWrite(IN1_Motor, LOW);
            digitalWrite(IN2_Motor, LOW);
            digitalWrite(EN, LOW);
            digitalWrite(D1, HIGH);
      }
}

void Pread()
{
      motor_angle[0] = Serial.read();
      delayMicroseconds(100000);
      motor_angle[1] = Serial.read();
      delayMicroseconds(100000);
      motor_angle[2] = Serial.read();
      position = atoi(motor_angle);
      packetsRecieved++;
}

void Sread()
{
      speed_value[0] = Serial.read();       
      delayMicroseconds(50000);
      speed_value[0] = Serial.read();
      delayMicroseconds(50000);
      speed_value[1] = Serial.read();
      delayMicroseconds(50000);
      speed_value[2] = Serial.read();
      velocity = atoi(speed_value);
      packetsRecieved++;
}

void serialCom()
{
      RUN = false;
      if(Serial.available())
      {
            char recieved = Serial.read();
            delayMicroseconds(100000);
            if(recieved == POSITION_HEADER)
            {
                  Pread();
                  Sread();
                  RUN = true;
            }
            else
            {
                  Serial.println("WRONG INPUT");
                  motor_angle[0] = 0;
            }
            
            if (packetsRecieved == (MESSAGE_PACKET_LENGTH))
            {
                  degree = (360 - position);
                  packetsRecieved = 0;
            }
            else
            {
                  Serial.print("Not enough packages");
                  Serial.println(packetsRecieved);
            }
      }
}

void setMotor(int pos, int Speed)
{
      if(degree < 0)
      {
            digitalWrite(IN1_Motor, HIGH);
            digitalWrite(IN2_Motor, LOW);
      }
      else
      {
            digitalWrite(IN1_Motor, LOW);
            digitalWrite(IN2_Motor, HIGH);
      }
      analogWrite(D2_Motor, Speed);
      digitalWrite(EN, HIGH);
      digitalWrite(D1, LOW);
      MotorEnable = true;
}

void encoderCycle()
{
      valA = digitalRead(a);
      valB = digitalRead(b);
      while(valA0 != valA || valB0 != valB)
      { 
            int valAB = 0;
            valAB = (10*valA) + valB;
            switch (valAB)
            {
                  case 0:
                  if(valAB0 == 1) 
                  {
                        teller = teller -1;
                  }
                  else 
                  {
                        teller = teller + 1;
                  }
                  break;
                  
                  case 1:
                  if(valAB0 == 11) 
                  {
                       teller = teller -1;
                  }
                  else 
                  {
                        teller = teller + 1;
                  }
                  break;
                  
                  case 11:
                  if(valAB0 == 10) 
                  {
                        teller = teller -1;
                  }
                  else 
                  {
                        teller = teller + 1;
                  }
                  break;

                  case 10:
                  if(valAB0 == 0) 
                  {
                        teller = teller -1;
                  }
                  else 
                  {
                        teller = teller + 1;
                  }
                  break;
            }
            if(teller >= 4) 
            {
                  teller = 0;
                  cycle += 1;
            }             
            if(teller <= -4)
            {
                  teller = 0;
                  cycle = cycle - 1;
            }
            if(cycle >= 360 || cycle <= -360)
            {

                   cycle = 0;     
            }
            valA0 = valA;
            valB0 = valB;
            valAB0 = (10*valA0) + valB0;
      }
}

I send as input "P160S100" P is for position and S is for speed.
The problem now is that the encoder rotate about 180 degrees when I specify that I want to rotate only 160 degrees. I know I need a PID controller soon anyway but it seems like the enoder counts to exactly 160 degrees and then stops. What am I doing wrong here? If the encoder rotates to 160 degrees and stops, why do it look like 180-200 degrees on my prototype?

I tried to attach a picture of my motor and encoder but I'm not sure how to attach a .jpg image.