rotary encoder (not always the same question) on attiny

hello,

I m looking as a mad for a library to use a rotary encoder on an attiny84. this chip only has 1 external interrup. so i can t use the standard libraries. i tried ClickEncoder but it rely on TimerOne and on the t84 there are just to 2bit timer

any help would be very appreciated

ClickEncoder doesn't rely on TimerOne. It relies on you calling it with a fixed interval. TimerOne is just a easy way to do that. But any other methode (and/or library) will do.

aster94:
to use a rotary encoder on an attiny84. this chip only has 1 external interrup. so i can t use the standard libraries.

Perhaps you could use pinChange interrupts?

...R

septillion:
ClickEncoder doesn't rely on TimerOne. It relies on you calling it with a fixed interval. TimerOne is just a easy way to do that. But any other methode (and/or library) will do.

ok to be honest i didn't check well the library i just saw that it didn't compile and i wrote here hoping that someone had a simple solution

so:

#include <ClickEncoder.h>
#include <TimerOne.h>

ClickEncoder *encoder;
int16_t oldEncPos, encPos;

#define pinA 6
#define pinB 5
#define pinSw 4 //switch

void timerIsr() {
  encoder->service();
}

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  encoder = new ClickEncoder(pinA, pinB, pinSw);

  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr);
  encoder->setAccelerationEnabled(1);

  Serial.print("Acceleration is ");
  Serial.println((encoder->getAccelerationEnabled()) ? "enabled" : "disabled");

  oldEncPos = -1;
}

void loop() {
  encPos += encoder->getValue();

  if (encPos != oldEncPos) {
    oldEncPos = encPos;
    Serial.print("Encoder Value: ");
    Serial.println(encPos);
  }

  ClickEncoder::Button buttonState = encoder->getButton();
  if (buttonState != ClickEncoder::Open) {
    Serial.print("Button: ");

#define VERBOSECASE(label) case label: Serial.println(#label); break;

    switch (buttonState) {

        VERBOSECASE(ClickEncoder::Pressed);
        VERBOSECASE(ClickEncoder::Released)
        VERBOSECASE(ClickEncoder::Clicked)

      case ClickEncoder::DoubleClicked:
        Serial.println("DoubleClicked");
        break;
        
      case ClickEncoder::Held:
        digitalWrite(13, HIGH);
        delay(100);
        break;
    }
  }
  digitalWrite(13, LOW);
}

so from the .cpp looks like that timerOne just do this:

Timer1.initialize(1000); //set the period in microsecond to do "something"
Timer1.attachInterrupt(timerIsr); //call the isr that update the encoder status

Well i don't want to look lazy but could someone suggest me a timer interrupt library for an 8bit timer? i don't want to work with registers

Robin2:
Perhaps you could use pinChange interrupts?

...R

i wasn't able to find a good library to do it!

aster94:
i wasn't able to find a good library to do it!

Does it need a library?

...R

I m back on the project and yes even if for many of you it is simple i need a livrary to use rotary encoders on attiny so if anyone know one it would be appreciated

Maybe there is some registers with different name between m328p and attiny84 because the same code/lib i used on the nano is not working

What are you using the encoder for? Is it a manual control? Position or RPM sensing for a motor? You may not need interrupts if the pulse frequency is slow enough (use polling). What encoder are you using?

groundFungus:
What are you using the encoder for? Is it a manual control? Position or RPM sensing for a motor? You may not need interrupts if the pulse frequency is slow enough (use polling). What encoder are you using?

Manual control for oled menu navigation, i didn t want to use the polling method since it would take more flash and i am on a tiny84

Post a link to the datasheet for the encoder you are using.

...R

it is the tipical rotary encoder found on a lot of web seller

it is with quadrature output i read:

//10, 0, 1, 11
//1, 0, 10, 11

for every step, depending on the direction

groundFungus:
What are you using the encoder for? Is it a manual control? Position or RPM sensing for a motor? You may not need interrupts if the pulse frequency is slow enough (use polling). What encoder are you using?

it seems that you were right, i add my solution for a rotary encoder without library, with polling method for future readers:

#define pinA 8
#define pinB 9
#define pinSw 7
byte aPos = 0;
byte bPos = 0;
byte port, oldPort;
byte encPos = 0;
byte oldEncPos = 0;

#define debug


void setup() {
  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);
  pinMode(pinSw, INPUT_PULLUP);
  //Serial.begin(115200);
}

void loop() {
  while (encPos < 50) {
    encMove();
    if (oldEncPos != encPos) {
      //Serial.println(encPos);
    }
    oldEncPos = encPos;
  }
  
  if (!digitalRead(pinSw)) {
    //Serial.println("pressed");
  }
}

void encMove() {
  port = PINB & 0x03;

  if (port != oldPort) {

    //Serial.print("port:\t"); Serial.println(port, BIN);
    //Serial.println();

    //10, 0, 1, 11
    //1, 0, 10, 11

    if (port == 0b10 && aPos) {
      //Serial.println("anticlockwise");
      encPos--;
      aPos = 0;
      bPos = 0;
    } else if (port == 0b10) {
      bPos = 1;
    }

    if (port == 0b1 && bPos) {
      //Serial.println("clockwise");
      encPos++;
      aPos = 0;
      bPos = 0;
    } else if (port == 0b1) {
      aPos = 1;
    }
  }
  oldPort = port;
}

it works pretty well and it is 868 byte with arduino uno so not so much, I will try to use it in my main project in the next days

Any suggestion about how to reduce the size of the sketch would be helpful

Here is an example of how I read my encoders. A little over 400 bytes with all the serial print stuff removed.

const byte aPin = 5;
const byte bPin = 4;

long encoderCnt = 0;

void setup()
{
  Serial.begin(115200);
  pinMode(aPin, INPUT_PULLUP);
  pinMode(bPin, INPUT_PULLUP);
}

void loop()
{
  if (readEncoder()) // count changed?
  {
    Serial.print("Encoder count =  ");
    Serial.println(encoderCnt);
  }
}

boolean readEncoder()
{
  boolean newEncode = false;  // new count flag
  static boolean last_aState = 0;

  boolean aState = digitalRead(aPin);
  boolean bState = digitalRead(bPin);

  if (aState != last_aState)
  {
    if (aState != bState)
    {
      encoderCnt++;
      //Serial.println("clockwise");
    }

    else
    {
      encoderCnt--;
      //Serial.println("counter clockwise");
    }

    last_aState = aState;
    newEncode = true;
  }
  return newEncode;
}

groundFungus:
Here is an example of how I read my encoders. A little over 400 bytes with all the serial print stuff removed.

const byte aPin = 5;

const byte bPin = 4;

long encoderCnt = 0;

void setup()
{
  Serial.begin(115200);
  pinMode(aPin, INPUT_PULLUP);
  pinMode(bPin, INPUT_PULLUP);
}

void loop()
{
  if (readEncoder()) // count changed?
  {
    Serial.print("Encoder count =  ");
    Serial.println(encoderCnt);
  }
}

boolean readEncoder()
{
  boolean newEncode = false;  // new count flag
  static boolean last_aState = 0;

boolean aState = digitalRead(aPin);
  boolean bState = digitalRead(bPin);

if (aState != last_aState)
  {
    if (aState != bState)
    {
      encoderCnt++;
      //Serial.println("clockwise");
    }

else
    {
      encoderCnt--;
      //Serial.println("counter clockwise");
    }

last_aState = aState;
    newEncode = true;
  }
  return newEncode;
}

tested, you could decrease the memory usage with direct port manipulation but maybe you don't need it
anyway i think we have different encoder type, using your sketch i get 2 steps of resolution (i mean i can t go from 1 to 2, it will jump to 3)

i made it more simply and it still works:

#define pinA 8
#define pinB 9
#define pinSw 7
byte encPos = 0;
byte oldEncPos = 0;
byte port, oldPort;


void setup() {
  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);
  pinMode(pinSw, INPUT_PULLUP);
  Serial.begin(115200);
}

void loop() {
  encMove();
  if (oldEncPos != encPos) {
    Serial.println(encPos);
  }
  oldEncPos = encPos;

  if (!digitalRead(pinSw)) {
    Serial.println("pressed");
  }
}

void encMove() {
  port = PINB & 0x03;

  //Serial.print("port:\t"); Serial.println(port, BIN);
  //Serial.println();
  //10, 00, 01, 11
  //01, 00, 10, 11

  if (oldPort == 0b11 && port == 0b01) {
    Serial.println("anticlockwise");
    encPos--;
  }

  else if (oldPort == 0b11 && port == 0b10) {
    Serial.println("clockwise");
    encPos++;
  }
  oldPort = port;
}