Help needed with dual rotary encoder code

Status
Not open for further replies.

rfresh737

Well-known member
My project needed a dual rotary encoder so I purchased one but I can't get it working the way I want.

I have only the smaller inner knob wired up. The push switch works fine.

However, when I turn the inner knob two things are happening which I don't want to happen:

1. My debug serial println shows the 'cw' direction on every other click, instead of on every click. I want each click to fire the 'cw' path.

2. Regardless of which direction I turn the inner knob, it always follows the same path, thus I never see the 'ccw' path. I need to be able to detect either direction, with each click.

Thank you for any help.

Code:
unsigned long currentTime;
unsigned long loopTime;
const int pin_A = 3;  // pin 3
const int pin_B = 4;  // pin 4
const int encoderSwitchPin = 5; //push button switch
unsigned char encoder_A;
unsigned char encoder_B;
unsigned char encoder_A_prev=0;

void setup()
{
  Serial.begin (9600);
  while (!Serial & millis() <4000)
  {
  }
  Serial.println("starting");
  pinMode(pin_A, INPUT_PULLUP);
  pinMode(pin_B, INPUT_PULLUP);
  pinMode(encoderSwitchPin, INPUT);

  digitalWrite(pin_A, HIGH); //turn pullup resistor on
  digitalWrite(pin_B, HIGH); //turn pullup resistor on
  digitalWrite(encoderSwitchPin, HIGH); //turn pullup resistor on
  
  currentTime = millis();
  loopTime = currentTime;
} 


void readEncoders()
{
	if (digitalRead(encoderSwitchPin))
	{
		//button is not being pushed
	}
	else
	{
		//button is being pushed
		Serial.println("pushed");
		delay(500);
	}
	currentTime = millis();
	if (currentTime >= (loopTime + 5))
	{
		encoder_A = digitalRead(pin_A);
		encoder_B = digitalRead(pin_B);
		if ((!encoder_A) && (encoder_A_prev))
		{
			// A has gone from high to low 
			if (encoder_B)
			{
				// B is high so clockwise
				Serial.println("cw");
			}
			else
			{
				// B is low so counter-clockwise      
				Serial.println("ccw");
			}
		}
		encoder_A_prev = encoder_A;     // Store value of A for next time    
		loopTime = currentTime;  // Updates loopTime
	}
}

void loop()
{
	readEncoders();
}
 
On a teensy you don't need the digital write on input pins. From looking at your code you will be having issues with rotation direction because you only monitor switch A for change, so steps where only switch B changed will be missed until you turn further and A changes.

The boring option is to fire the encoder library at the problem https://www.pjrc.com/teensy/td_libs_Encoder.html, otherwise you'd need to look at both switch A and switch B state changes, and determine rotation direction based on the current partner switch state.

This will also be complicated by the fact that many encoders have detentes that do not much the encoder hardware so you can get steps that don't encode a change, or encode two, which the polling logic may miss. The Encoder library uses interrupts to make missed steps less likely.
 
digitalRead() will return the value at the sampling instant the port is checked.

An interrupt could be enabled for change - but the rate of change could get ahead of your ability to react. Post #2 points to a hardware based library designed for encoders.
 
I am now using the code from the referenced library in post #2.

However, I'm still getting 2 debug lines per click:
Here is 3 clicks to the right:

Uploading to I/O board
Opening port
Port open
TwoKnobs Encoder Test:
Left = 0, Right = 0

Left = -1, Right = 0 <-click1
Left = -2, Right = 0 <-click1
Left = -3, Right = 0 <-click 2
Left = -4, Right = 0 <-click 2
Left = -5, Right = 0 <-click 3
Left = -6, Right = 0 <-click 3


My code:

Code:
#include <Encoder.h>

// Change these pin numbers to the pins connected to your encoder.
//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability
Encoder knobLeft(3, 4);
Encoder knobRight(7, 8);
//   avoid using pins with LEDs attached

void setup() {
  Serial.begin(9600);
  while (!Serial & millis() <4000)
  {
  }
  Serial.println("TwoKnobs Encoder Test:");
}

long positionLeft  = -999;
long positionRight = -999;

void loop()
{
  long newLeft, newRight;
  newLeft = knobLeft.read();
  newRight = knobRight.read();
  if (newLeft != positionLeft || newRight != positionRight)
  {
    Serial.print("Left = ");
    Serial.print(newLeft);
    Serial.print(", Right = ");
    Serial.println(newRight);
    positionLeft = newLeft;
    positionRight = newRight;
	delay(500);
  }
  // if a character is sent from the serial monitor,
  // reset both back to zero.
  if (Serial.available())
  {
    Serial.read();
    Serial.println("Reset both knobs to zero");
    knobLeft.write(0);
    knobRight.write(0);
  }
}
 
There may be some obvious reason - not seeing that - I'd print out "positionLeft " on each change. That will show the value of each click to tell if it is changing sensibly. It might be your encoder or wiring?

Also it should be safe to comment out the delay() - then the time between the change will follow with the debug output.

Assuming there are no LED's on those pins or other connected devices? What Teensy # are you using?
 
Your original code would kind of work for an encoder with 4 counts per detent (2 counts per detent is also very common). You likely have your A / B / C pins mixed up - what you are seeing would be typical symptom.

Your code uses a different edge of the A channel to switch depending on the rotation direction - not good. Look at an encoder phase diagram, e.g. here.
 
Yes, I moved my A B wires onto different pins and now its working...thanks everyone...!!! I ended up using the code from the Lib referenced in the second post.
 
Status
Not open for further replies.
Back
Top