Help with using 1 encoder to set 2 values

I am at the 1st stage of a project where I want to use 1 encoder to set 2 values.
I got it working (in a wrong way) in that I can set the 1st value, but when I select to set the 2nd value, it starts at the same value as the 1st value, then I can adjust it up or down.

How can I get the 2nd value to start at 0?
Thank you for any advice.
(Joe, newbie)


```cpp
// -----
// SimplePollRotatorLCD.ino - Example for the RotaryEncoder library.
// This class is implemented for use with the Arduino environment.
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// -----
// 18.01.2014 created by Matthias Hertel
// -----

// This example checks the state of the rotary encoder in the loop() function.
// The current position is printed on output when changed.

// Hardware setup:
// Attach a rotary encoder with output pins to A2 and A3.
// The common contact should be attached to ground.

#include <Wire.h>
#include <LiquidCrystal.h>
#include <RotaryEncoder.h>
LiquidCrystal lcd(12, 13, 7, 6, 5, 8);

RotaryEncoder encoder(A2, A3);

#define selectSW1 9
int currentStateSW1;

static int pos = 0;
int newPos = 0;
static int pos1 = 0;
int newPos1 = 0;

void setup() {
  pinMode(selectSW1, INPUT_PULLUP);

  lcd.begin(16, 2);
  lcd.print("Dual Value 1Enc");
  delay(2000);
  lcd.clear();

  // You may have to modify the next 2 lines if using other pins than A2 and A3
  PCICR |= (1 << PCIE1);                      // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
  PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);  // This enables the interrupt for pin 2 and 3 of Port C.
}  // setup()



// The Interrupt Service Routine for Pin Change Interrupt 1
// This routine will only be called on any signal change on A2 and A3: exactly where we need to check.
ISR(PCINT1_vect) {
  encoder.tick();  // just call tick() to check the state.
}  // ISR



// Read the current position of the encoder and print out when changed.
void loop() {
  currentStateSW1 = digitalRead(selectSW1);

  if (currentStateSW1 == 1) {
    lcd.setCursor(15, 1);
    lcd.print(" ");
    lcd.setCursor(15, 0);
    lcd.print((char)127);  // (char)127 = ASCII <-

    Line1();
  }

  else {
    lcd.setCursor(15, 0);
    lcd.print(" ");
    lcd.setCursor(15, 1);
    lcd.print((char)127);  // (char)127 = ASCII <-

    Line2();
  }

}  // loop ()


/**** Print 1st setting on Line1 ***/
void Line1() {
  newPos = encoder.getPosition();
  if (pos != newPos) {
    lcd.setCursor(0, 0);
    lcd.print(newPos);
    pos = newPos;
  }
}  //Line1 ()


/**** Print 2nd setting on Line2 ***/
void Line2() {
  newPos1 = encoder.getPosition();
  if (pos1 != newPos1) {
    lcd.setCursor(0, 1);
    lcd.print(newPos1);
    pos1 = newPos1;
  }
}  //Line2 ()

Try and apply debouncing to the switch readings.

The counting works 100%, it is only that the 2nd value starts at whatever I set the 1st value to.
I want the 2nd value to start at 0.

Hi @jdev99 ,

when you change between line1() and line2() the encoder keeps its last position.

You have to change the encoder position depending on which of the two values you want to change.

There is a function called

  // adjust the current position
  void setPosition(long newPosition);

in the Rotary Encoder lib.

Feel free to try this (not tested just coded :wink: ):

// -----
// SimplePollRotatorLCD.ino - Example for the RotaryEncoder library.
// This class is implemented for use with the Arduino environment.
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// -----
// 18.01.2014 created by Matthias Hertel
// -----

// This example checks the state of the rotary encoder in the loop() function.
// The current position is printed on output when changed.

// Hardware setup:
// Attach a rotary encoder with output pins to A2 and A3.
// The common contact should be attached to ground.

#include <Wire.h>
#include <LiquidCrystal.h>
#include <RotaryEncoder.h>
LiquidCrystal lcd(12, 13, 7, 6, 5, 8);

RotaryEncoder encoder(A2, A3);

#define selectSW1 9
int currentStateSW1;

static int pos = 0;
int newPos = 0;
static int pos1 = 0;
int newPos1 = 0;




void setup() {
  pinMode(selectSW1, INPUT_PULLUP);

  lcd.begin(16, 2);
  lcd.print("Dual Value 1Enc");
  delay(2000);
  lcd.clear();

  // You may have to modify the next 2 lines if using other pins than A2 and A3
  PCICR |= (1 << PCIE1);                      // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
  PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);  // This enables the interrupt for pin 2 and 3 of Port C.
}  // setup()



// The Interrupt Service Routine for Pin Change Interrupt 1
// This routine will only be called on any signal change on A2 and A3: exactly where we need to check.
ISR(PCINT1_vect) {
  encoder.tick();  // just call tick() to check the state.
}  // ISR

// setPos changes between 1 and 2 depending on whether line1() or line2() is called
byte setPos = 0;

// Read the current position of the encoder and print out when changed.
void loop() {
  currentStateSW1 = digitalRead(selectSW1);

  if (currentStateSW1 == 1) {
    lcd.setCursor(15, 1);
    lcd.print(" ");
    lcd.setCursor(15, 0);
    lcd.print((char)127);  // (char)127 = ASCII <-

    Line1();
  }

  else {
    lcd.setCursor(15, 0);
    lcd.print(" ");
    lcd.setCursor(15, 1);
    lcd.print((char)127);  // (char)127 = ASCII <-

    Line2();
  }

}  // loop ()


/**** Print 1st setting on Line1 ***/
void Line1() {
  if (setPos != 1){
    setPos = 1;
    encoder.setPosition(pos);
  }
  newPos = encoder.getPosition();
  if (pos != newPos) {
    lcd.setCursor(0, 0);
    lcd.print(newPos);
    pos = newPos;
  }
}  //Line1 ()


/**** Print 2nd setting on Line2 ***/
void Line2() {
 if (setPos != 2){
    setPos = 2;
    encoder.setPosition(pos1);
  }
  newPos1 = encoder.getPosition();
  if (pos1 != newPos1) {
    lcd.setCursor(0, 1);
    lcd.print(newPos1);
    pos1 = newPos1;
  }
}  //Line2 ()

The changes to your code are:

// setPos changes between 1 and 2 depending on whether line1() or line2() is called
byte setPos = 0;

and

void Line1() {
  if (setPos != 1){
    setPos = 1;
    encoder.setPosition(pos);
  }
// ...

void Line2() {
 if (setPos != 2){
    setPos = 2;
    encoder.setPosition(pos1);
  }
//...

This way the encoder position should be set to the last known pos or pos1 value.

You can check a slightly improved version on Wokwi
https://wokwi.com/projects/390444791965276161

with a separate function to print the position data to the LCD (clears about 10 characters at the beginning of a line to remove unwanted numbers from previous data) and prints the start values in setup().

Sketch
/*
  Forum: https://forum.arduino.cc/t/help-with-using-1-encoder-to-set-2-values/1227169/4
  Wokwi: https://wokwi.com/projects/390444791965276161

*/

// -----
// SimplePollRotatorLCD.ino - Example for the RotaryEncoder library.
// This class is implemented for use with the Arduino environment.
// Copyright (c) by Matthias Hertel, http://www.mathertel.de
// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
// More information on: http://www.mathertel.de/Arduino
// -----
// 18.01.2014 created by Matthias Hertel
// -----

// This example checks the state of the rotary encoder in the loop() function.
// The current position is printed on output when changed.

// Hardware setup:
// Attach a rotary encoder with output pins to A2 and A3.
// The common contact should be attached to ground.

#include <Wire.h>
#include <LiquidCrystal.h>
#include <RotaryEncoder.h>
LiquidCrystal lcd(12, 13, 7, 6, 5, 8);

RotaryEncoder encoder(A2, A3);

#define selectSW1 9
int currentStateSW1;

static int pos = 0;
int newPos = 0;
static int pos1 = 0;
int newPos1 = 0;
// setPos changes between 1 and 2 depending on whether line1() or line2() is called
byte setPos = 0;




void setup() {
  pinMode(selectSW1, INPUT_PULLUP);
  lcd.begin(16, 2);
  lcd.print("Dual Value 1Enc");
  delay(2000);
  lcd.clear();
  printPos(pos,0);
  printPos(pos1,1);

  // You may have to modify the next 2 lines if using other pins than A2 and A3
  PCICR |= (1 << PCIE1);                      // This enables Pin Change Interrupt 1 that covers the Analog input pins or Port C.
  PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);  // This enables the interrupt for pin 2 and 3 of Port C.
}  // setup()



// The Interrupt Service Routine for Pin Change Interrupt 1
// This routine will only be called on any signal change on A2 and A3: exactly where we need to check.
ISR(PCINT1_vect) {
  encoder.tick();  // just call tick() to check the state.
}  // ISR


// Read the current position of the encoder and print out when changed.
void loop() {
  currentStateSW1 = digitalRead(selectSW1);

  if (currentStateSW1 == 1) {
    lcd.setCursor(15, 1);
    lcd.print(" ");
    lcd.setCursor(15, 0);
    lcd.print((char)127);  // (char)127 = ASCII <-

    Line1();
  }

  else {
    lcd.setCursor(15, 0);
    lcd.print(" ");
    lcd.setCursor(15, 1);
    lcd.print((char)127);  // (char)127 = ASCII <-

    Line2();
  }

}  // loop ()


/**** Print 1st setting on Line1 ***/
void Line1() {
  if (setPos != 1){
    setPos = 1;
    encoder.setPosition(pos);
  }
  newPos = encoder.getPosition();
  if (pos != newPos) {
    printPos(newPos,0);
    pos = newPos;
  }
}  //Line1 ()


/**** Print 2nd setting on Line2 ***/
void Line2() {
 if (setPos != 2){
    setPos = 2;
    encoder.setPosition(pos1);
  }
  newPos1 = encoder.getPosition();
  if (pos1 != newPos1) {
    printPos(newPos1,1);
    pos1 = newPos1;
  }
}  //Line2 ()


void printPos(int aPos, byte Row){
    lcd.setCursor(0, Row);
    lcd.print("          ");
    lcd.setCursor(0, Row);
    lcd.print(aPos);
}
1 Like

"ec2021"
I saw that function, but was not sure how to use it. :upside_down_face:

Thank you very much, works 100%
Appreciate your assistance. :wink: :+1:

Joe

No worries, however I second @Railroader 's concerns regarding the debouncing!

I' m not sure what kind of switches you use but it is quite likely that they will have to be debounced to avoid a reaction of the sketch to spikes from switching.

The way your program is written at the moment it will have bouncng effects but not show them to you, It might become a problem if you add further functionality!

You'd better have a look at "debouncing" to avoid issues in future ... :wink:

Good luck!
ec2021

P.S.: It is actually not a big deal ...

Use this:

byte switchState(){
  static unsigned long lastChange = 0;
  static byte lastState = HIGH;
  static byte state = HIGH;
  byte actState = digitalRead(selectSW1);
  if (actState != lastState) {
    lastChange = millis();
    lastState = actState;
  }
  if (actState != state && millis()-lastChange > 20){
    state = actState;
  }
  return (state ? 1 : 0);
}

and replace digitalRead() in loop() by switchState()

void loop() {
  currentStateSW1 = switchState(); //digitalRead(selectSW1);

The function takes care that the switch state is only changed after a stable level for at least 20 ms.

If you feel the task is solved please mark the thread as "solved" ... :wink:

1 Like

It seems like the author off that encoder library did take it into account.
I can spin the encoder anyway I like, it does not loose or skip a count. :+1:

https://www.mathertel.de/Arduino/RotaryEncoderLibrary.aspx

As you are not using a library function of Rotary Encoder lib but digitalRead() to check the status of the switch there is no debouncing... :wink:

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.