Trying to get one encoder to act like two

I’m trying to get one encoder to act like two when the button is held down and knob turned.
When button released and knob turned, I want one value to change, and when button held down and knob turned I want a second value to be changed. Right now both are changing at once and I think that is being caused by the SW pin om the encoder going to ground each time the knob is turned.
Is there a way to do this?

Here’s the code.

#include <Encoder.h>
Encoder knobLeft(5, 6);
Encoder knobRight(7, 8);
int buttonPin = 2;
void setup() {
  Serial.begin(9600);
  Serial.println("three Knobs Encoder Test:");
}

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

void loop() {
  long newLeft, newRight, two, three;
  newLeft = (knobLeft.read() / 4 * 10);
  newRight = (knobRight.read() / 4);
  Two = constrain(newRight, 0, 360);
  Three = (newLeft * 0.01);  
while (digitalRead(buttonPin) == LOW) {
 newLeft = qnh;
}
  if (newLeft != positionLeft || newRight != positionRight) {
    Serial.print("one = ");
    Serial.print(newLeft);
     Serial.print(", two= ");
    Serial.print(qnh), 2;
    Serial.print(", three= ");
    Serial.print(head);
    Serial.println();
    positionLeft = newLeft;
    positionRight = newRight;
  }
  // 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);
  }
}

How is this single encoder wired?
Your code has two encoders defined but if you have wired pin 5 to 7 and 6 to 8 then the results you get are to be expected.

The problem is the use of the Encoder library, you will have to change the code in that to look at the push button and see what counter to increment.

However the big problem is that reading encoders depends on knowing the last state of the encoder and if you use the same physical encoder then that last state is wrong when you change over so I don’t think your trick will work anyway.

Grumpy_Mike:
However the big problem is that reading encoders depends on knowing the last state of the encoder and if you use the same physical encoder then that last state is wrong when you change over so I don’t think your trick will work anyway.

You would have to code it such that if the state of the button has changed (state change example) since the last change in the encoder (in the interrupt if it's interrupt driven) then you don't increment on the first pulse you just record a new last state. It would necessitate dropping the first pulse on every switch, but I don't think there is any other way.

I have two encoders wired in. They both work great togeather and one at a time. The issue occurs if I hold button down and turn.

Is there a way to get a 3rd encoder to work on the uno?
Or would I be better off using an Attiny with a 3rd encoder linked to the uno to provide that value?

Qhergt1987:
Is there a way to get a 3rd encoder to work on the uno?

Yes. I wrote some code for a 1284P that reads two encoders on spinning motors using pin change interrupts. It could easily be extended to three encoders on an UNO if you can use 6 pins on the same port. Give me a minute to look it up.

This wouldn’t be exactly what you need. It obviously makes reverences to my robot and not whatever you’re doing. But you should be able to see how I wrote the ISR to quickly figure out which encoder fired and in which direction.

It does require that all of the encoders be set up on the same port. On the UNO that pretty much limits you to PortD on pins 2 through 7 (unless you don’t use Serial and want to use 0 and 1), or PortB on pins 8 through 13. But no combinations of the two.

#define LEFT_INT_MASK 0x80
#define LEFT_B_MASK 0x40
#define RIGHT_INT_MASK 0x20
#define RIGHT_B_MASK 0x10

volatile uint8_t lastPortRead = 0;

volatile int32_t leftCounter = 0;
volatile int32_t rightCounter = 0;


ISR(PCINT2_vect){

	uint8_t portRead = PINC;

	uint8_t whoFired = portRead ^ lastPortRead;
	uint8_t pinsDiff = portRead ^ (portRead << 1); // Will have bits 5 or 7 set if pins on right motor or left motor are diff.  Cleared if same.

	if (whoFired & LEFT_INT_MASK) {
		boolean forw = false;
		if (pinsDiff & LEFT_INT_MASK) {
			leftCounter++;
			forw = true;
		} else {
			leftCounter--;
		}
	}
	if (whoFired & RIGHT_INT_MASK) {
		boolean forw = false;
		if (pinsDiff & RIGHT_INT_MASK) {
			rightCounter++;
			forw = true;
		} else {
			rightCounter--;
		}
	}

	lastPortRead = portRead;
}


void setupPCint(){

	PCICR = PCICR | (1 << PCIE2);   //  Turn on pin change interrupt 2
	PCIFR = (1 << PCIF2);   // Clear any pending interrupt flag for pin change interrupt 2
	PCMSK2 = (1 << PCINT23) | (1 << PCINT21);  // Set interrupts for pins 21 and 23 at bits 5 and 7.  Bits 4 and 6 will be the other pins.

}


int32_t getLeftMotorCount(){
	int32_t retval = 0;
	// Disable interrupt
	cli();
	retval = leftCounter;
	sei();
	return retval;
}

int32_t getRightMotorCount(){
	int32_t retval = 0;
	// Disable interrupt
	cli();
	retval = rightCounter;
	sei();
	return retval;
}

If you’re having trouble following the port reads, it’s mimicking the same idea as this code where we determine the direction by seeing which pin is leading.

Delta_G:
This wouldn’t be exactly what you need. It obviously makes reverences to my robot and not whatever you’re doing. But you should be able to see how I wrote the ISR to quickly figure out which encoder fired and in which direction.

It does require that all of the encoders be set up on the same port. On the UNO that pretty much limits you to PortD on pins 2 through 7 (unless you don’t use Serial and want to use 0 and 1), or PortB on pins 8 through 13. But no combinations of the two.

#define LEFT_INT_MASK 0x80

#define LEFT_B_MASK 0x40
#define RIGHT_INT_MASK 0x20
#define RIGHT_B_MASK 0x10

volatile uint8_t lastPortRead = 0;

volatile int32_t leftCounter = 0;
volatile int32_t rightCounter = 0;

ISR(PCINT2_vect){

uint8_t portRead = PINC;

uint8_t whoFired = portRead ^ lastPortRead;
uint8_t pinsDiff = portRead ^ (portRead << 1); // Will have bits 5 or 7 set if pins on right motor or left motor are diff.  Cleared if same.

if (whoFired & LEFT_INT_MASK) {
	boolean forw = false;
	if (pinsDiff & LEFT_INT_MASK) {
		leftCounter++;
		forw = true;
	} else {
		leftCounter--;
	}
}
if (whoFired & RIGHT_INT_MASK) {
	boolean forw = false;
	if (pinsDiff & RIGHT_INT_MASK) {
		rightCounter++;
		forw = true;
	} else {
		rightCounter--;
	}
}

lastPortRead = portRead;

}

void setupPCint(){

PCICR = PCICR | (1 << PCIE2);   //  Turn on pin change interrupt 2
PCIFR = (1 << PCIF2);   // Clear any pending interrupt flag for pin change interrupt 2
PCMSK2 = (1 << PCINT23) | (1 << PCINT21);  // Set interrupts for pins 21 and 23 at bits 5 and 7.  Bits 4 and 6 will be the other pins.

}

int32_t getLeftMotorCount(){
int32_t retval = 0;
// Disable interrupt
cli();
retval = leftCounter;
sei();
return retval;
}

int32_t getRightMotorCount(){
int32_t retval = 0;
// Disable interrupt
cli();
retval = rightCounter;
sei();
return retval;
}

Basically I’ll have to create a “Center Counter” for a 3rd encoder?

Qhergt1987:
Basically I'll have to create a "Center Counter" for a 3rd encoder?

Yeah, something like that.

Qhergt1987:
Basically I'll have to create a "Center Counter" for a 3rd encoder?

Qhergt1987:
I'm trying to get one encoder to act like two when the button is held down and knob turned.
When button released and knob turned, I want one value to change, and when button held down and knob turned I want a second value to be changed.

So, is the idea to have one physical encoder for three different values?

Yes,
Or just have 3 seperate encoders.
I tried modifying the two encoder example from the encoder.h library but that didnt work.

Why not use the debounced pushbutton to cycle a counter through 0-1-2-0-1-2...

if(counter == zero) valueA is manipulated,
if(counter == one) valueB is manipulated,
and so on.

A minimal visual feedback would be some LEDs to show which counter is active.

Could do.

Would prefer to have 3 seperate encoders at least.