Direction change detection on rotary encoder

Hi to all. I’m hoping that my Arduino (Uno) isn’t damaged or something, but I’m having issues getting correct direction on rotary encoder (old motor from HDD). I’ve hooked it up based on this schematic:

I’ve picked code somewhere on the web and modified it a bit:

#define DIRECTION_CHANGE_TIMEOUT 300000

//if disk rotates in different position, reset the MIDI data counter
int hddDirection = 0;

//for loop variables
int j = 0;
int k = 0;
 
//current state of MIDI counter
int currpos[6] = {
  0xE0, 0x00, 0x40, 0xE0, 0x00, 0x40 };
 
// Stores the digital values
int digA[2] = {0, 0};
int digB[2] = {0, 0};
 
// Stores the last four 'patterns'
int prevPatterns[8] = {
  0, 0, 0, 0, 0, 0, 0, 0};
 
// Stores the current 'pattern'
int currPattern[2] = {0, 0};

unsigned long int time = 0;
unsigned long int spinTime = 0;
 

 
void setup()
{
 
  pinMode(18, INPUT);
  pinMode(19, INPUT);
  
  Serial.begin(9600);
 
}
 
 
void loop()
{
 
  digA[0] = digitalRead(18);
  digB[0] = digitalRead(19);

 
  if (digA[j] && !digB[j]) {
    // Only A is on
    currPattern[j] = 1;
  }
 
 
  else if (!digA[j] && digB[j]) {
    // Only B is on
    currPattern[j] = 3;
  }
 
  else if (digA[j] && digB[j]) {
    // Both A and B are on
    currPattern[j] = 2;
  }
 
  else if (!digA[j] && !digB[j]) {
    // Neither A nor B are on
    currPattern[j] = 4;
  }
 
 
 
  if (currPattern[j] != prevPatterns[4*j]) {
    // The current pattern is different to the last one, and is not just a repetition.
 
    // Shift patterns up, where prevPatterns[4*j] is the newest
 
 
 
    for (int k = 3; k > 0; k--)
 
    {
      prevPatterns[k+(4*j)] = prevPatterns[(k+(4*j)) - 1];
    }
 
 
    prevPatterns[4*j] = currPattern[j];
 
    if (arrayMatches(prevPatterns, 3, 2, 1, 4) || arrayMatches(prevPatterns, 2, 1, 4, 3) || arrayMatches(prevPatterns, 1, 4, 3, 2) || arrayMatches(prevPatterns, 4, 3, 2, 1))
 
    {
             
      if ((hddDirection == 0 || hddDirection == 2) && micros() - time > DIRECTION_CHANGE_TIMEOUT) {
        currpos[1+2*j] = 0x00;
        currpos[2+2*j] = 0x40;
        hddDirection = 1;
      }
      
      if (hddDirection == 1) {
      time=micros();
      // Direction A (clockwise)
      Serial.print("<"); 
 
     if (currpos[2+2*j]==127)
 
      {
        if (currpos[1+2*j]!=127)  {
          currpos[2+2*j]=0x7F;
          midiCC(0xE0+j, currpos[1+2*j]+=1, currpos[2+2*j]);
        }
 
        else {
          currpos[1+2*j]=0x00;
          currpos[2+2*j]=0x40;
        }
 
 
      }  
 
      else if (currpos[2+2*j]!=127)  midiCC(0xE0+j, currpos[1+2*j], currpos[2+2*j]+=1);
 
 
    }
  }
 
 
 
 
 
    else  if (arrayMatches(prevPatterns, 1, 2, 3, 4) || arrayMatches(prevPatterns, 2, 3, 4, 1) || arrayMatches(prevPatterns, 3, 4, 1, 2) || arrayMatches(prevPatterns, 4, 1, 2, 3))
 
 
    {
      
   
      if ((hddDirection == 0 || hddDirection == 1) && micros() - time > DIRECTION_CHANGE_TIMEOUT) {
        hddDirection=2;
        currpos[1+2*j] = 0x00;
        currpos[2+2*j] = 0x40;
      }
      
      if (hddDirection == 2) {
      
      time=micros();
      // Direction B (anticlockwise)
      Serial.print(">");
 
      if (currpos[2+2*j]!=0)  {
        currpos[1+2*j]=0x7F;
        midiCC (0xE0+j, currpos[1+2*j], currpos[2+2*j]-=1);
      }
 
      else {
 
        if (currpos[1+2*j]!=0)  {
          currpos[2+2*j]=0;
          midiCC (0xE0+j, currpos[1+2*j]-=1, 0x00);
        }
 
        else                {
          currpos[1+2*j]=0x00;
          currpos[2+2*j]=0x40;                
        }
 
      }
     
    }}
     
    
  } 
 
 
}
 
 
 
 
// this function sends a Midi CC.
void midiCC(char CC_data, char c_num, char c_val){
  //Serial.write(CC_data);
  //Serial.write(c_num);
  //Serial.write(c_val);
 
}
 
 
int arrayMatches(int array[], int a, int b, int c, int d) {
  // If a, b, c & d match array items 0 to 3, then return true.
  return (array[4*j] == a && array[1+4*j] == b && array[2+4*j] == c && array[3+4*j] == d);
}

For now I’ve commented out the midi CC function and I’m using only “<” and “>” to display the direction on serial monitor. Now, with my modifications (defined timeout) code works nicely when I spin the disk really fast, ie:

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>

Notice how nicely it goes to other direction.
Now, if I spin the disk slowly, I’m getting this (I’m spinning it in one direction only!):

<>>>>><<<<<<<<<<<>>

What have I done wrong?

I have done this but I only kept the current state and the last state.

the 4 states are - 0, 1, 3, 2

If the current state = 1 and the last state = 0 you are rotating forward. If the current state = 1 and the last state = 3 then you are rotating reverse.

Or you can try this - If A turns on and B is off then you are rotating forward. If A turns on and B is On the you are rotating in reverse,If A turns off and B is on then you are rotating forward. If A turns off and B is Off the you are rotating in reverse. you are still looking for state changes -

I've tried this sketch but nothing is happening when I spin the disk. http://www.thedeanda.com/2011/12/arduino-rotary-encoders-rev1.html

Do your encoders supply 5V out A & B? the sketch looks to be for open-collector type encoders.

Some encoders are sourcing type outputs - meaning you supply 5 V tot he encoder and the encoder supplies 5V out the A & B wires.

Some are sinking type - the encoder is tied to ground, and A & B pull the wires to ground when active.

kf2qd:
Do your encoders supply 5V out A & B? the sketch looks to be for open-collector type encoders.

Some encoders are sourcing type outputs - meaning you supply 5 V tot he encoder and the encoder supplies 5V out the A & B wires.

Some are sinking type - the encoder is tied to ground, and A & B pull the wires to ground when active.

I think the OP is using a hard-drive motor -as- an encoder... (!)

cr0sh:

kf2qd:
Do your encoders supply 5V out A & B? the sketch looks to be for open-collector type encoders.

Some encoders are sourcing type outputs - meaning you supply 5 V tot he encoder and the encoder supplies 5V out the A & B wires.

Some are sinking type - the encoder is tied to ground, and A & B pull the wires to ground when active.

I think the OP is using a hard-drive motor -as- an encoder… (!)

Yes indeed. The sketch I’ve mentioned actually does work, it was just I had no idea what interrupt pins actually are so I’ve connected everything to wrong place. Anyways I’m using this code now:

void HDDJ::getHDDJdata() {


      long now = millis();
    if (now > nextEncoderTime) {
      //reset fast debounce since there was a pause
      if (now > nextEncoderTime + ENCODER_FAST_TIMEOUT) {
        encoderLastDirection = 0;
      }

      int a = digitalRead(_hddj1pin1);
      int b = digitalRead(_hddj1pin2);


      if ((a == HIGH && a == b) || (a == LOW && a == b))

      {
        encoderDirection = 1;
      }

      else if ((a == HIGH && b != a) || (a == LOW && a != b))

      {
        encoderDirection = -1;
      }

      else encoderDirection = 0;

         boolean fastMode = abs(encoderLastDirection) > ENCODER_STEPS_TILL_FAST;

      //if we get a random direction change while fast, ignore it
      if (fastMode  && (encoderLastDirection > 0 && encoderDirection < 0 || encoderLastDirection < 0 && encoderLastDirection > 0))
      {
        encoderDirection = 0;
      }
      encoderLastDirection += encoderDirection;

         if (encoderDirection != 0) {
        nextEncoderTime = now + (fastMode ? ENCODER_DEBOUNCE_FAST : ENCODER_DEBOUNCE);
        Serial.print(encoderDirection);

      }

    }

}

Code works quite well at slow speeds but kinda fails at fast rotation. Oh the frustration.

I’ve also tried to understand this code:
http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino/comment-page-2#comment-13139

But I can’t understand half of it, so I would be gratefull if someone could translate the read_encoder() function into something understandable. I don’t know how to re-write it using digitalRead.

The ReadEncoder section is doing some slight of hand to hold 2 values in 1 variable…

static uint8_t old_AB = 0;
old_AB <<= 2; //remember previous state Shift the bits so bits 0 & 1 are now in bits 2 & 3
old_AB |= ( ENC_PORT & 0×03 ); //add current state Read the current encoder state into bits 0 & 1
return ( enc_states[( old_AB & 0x0f )]); clear the upper 4 bits and return the value of the last state and current state of the encoder

Actually bits 2 & 3 will be zero because the variable is cleared to 0 (zero) each time through the function… Only bits 0 & 1 will have a value.
Must be that the original programmer either wrote broken code, or changed the operation of the code and never cleaned it up…

Oops - been a while since I played with static… Too much other stuff cluttering up the files.

kf2qd:
Actually bits 2 & 3 will be zero because the variable is cleared to 0 (zero) each time through the function.

Which bit of code is doing this? Did you miss the 'static'?

http://arduino.cc/en/Reference/Static "Variables declared as static will only be created and initialized the first time a function is called."