Connecting rotary encoder to Arduino Nano

Hello,
I would like to connect a rotary encoder with a built-in switch to an Arduino Nano.
Is there a way to trigger all three events from the encoder (both rotations and the press of the switch) on interrupts?
I am a bit confused about the number of interrupts, as well as their description regarding "digital" versus "analog" pins. Am I out of luck here, and I can only use two pins for interrupts on a Nano?

Thanks

What rotary encoder do you have? There must be dozens if not hundreds of different rotary encoders. Is it like this?

Why interrupts? Beginners often think that interrupts are necessary where they are not. For instance a rotary encoder that a human turns is not a good use for an interrupt.

It is one that has three outputs, plus two pins for power.

I have the left-right reading working, but how to configure the interrupt for the switch, is my question.

#include "RotaryEncoder.h"
#define RTRY_ENC_CLK A2
#define RTRY_ENC_DT A3
RotaryEncoder encoder(RTRY_ENC_CLK, RTRY_ENC_DT);

void setup () {
  PCICR |= (1 << PCIE1); // enable pin change interrupts
  PCMSK1 |= (1 << PCINT10); // enable PCI for pin A2
  PCMSK1 |= (1 << PCINT11); // enable PCI for pin A3
}

ISR(PCINT1_vect) {
  encoder.tick();
}

void loop () {
    // this works
    RotaryEncoder::Direction direction = encoder.getDirection();
    // ...
}

The library has no function for the switch. That needs to be handled separately like any momentary switch.

I use the state change detection method, but for active low switches.

Watch DroneBot Workshop’s video on rotary encoders on YouTube (trust me, it’s worth it)

My question is not really about the encoder or the library, but how to make a third interrupt work on this board...

I have seen a schedule of which Arduino PCBs support what interrupts, and on what pins. It might have been in the reference material in attachInterrupt or interrupt

The code that you posted uses pin change interrupts. Most any pin can be an interrupt with pin change interrupts.

Hi, @thokari
Welcome to the forum.

Why do you think you need an interrupt for a human pressed switch?
Forget about an interrupt and just code for a switch input.

I think you are over thinking the problem.

Tom... :smiley: :+1: :coffee: :australia:

In general: yes. But also: ISRs are not evil in any case.
I used in my application a ISR, which is called every 500us, to evaluate two (optical) rotary encoders and four switches - within 20us (and I'm sure my code is not highly optimized). So the ISR puts a burden of 4% CPU load. A load which is - from my point of view - not really something to worry about. Within my application, a execution of one loop (within loop()) takes 10ms, so, with ISR, I can be sure not to miss anything from the inputs.

Remark: machanical rotary encoders bounce as well, and debouncing them is some topic on its own. I avoided that by using optical one because they do not bounce. Ok, they are a little more expensive, sure, but make life easy, in particular to obtain maximum resolution ( x4 ).
(ISR-) Result of rotary encoder and switches is then further proccessed in loop().

Most definitely not overthinking, because my microcontroller is busy for 20ms sampling audio data every loop, and then spends 1-2ms at most sending that data over a radio connection.
I experienced from my own testing, that trying to handle inputs from the encoder or a button during the remaining time in the loop leaves a lot to be desired in terms of user experience.

I definitely need interrupts, and I have solved my problem, using this library:

I was unable to configure another pin like the ones I did.
I realize that I was not even using the hardware interrupt pins (D2, D3), but instead the A2, and A3 pins, so technically, I had it working, but for reasons that I do not understand I was not able to translate that to a third input, without said library.
I can say though that this library makes for nicely readable code, and there is not much in it anways, so I will stick with it.

Thanks also to @groundFungus for mentioning again that PCI can be used for every pin. I found that out by myself, eventually, but I think the documentation could be a bit more forthcoming about this.

Code that I am using, resulting in perfect user experience, no bouncing, instant reaction, always.
Of course the audio sampling suffers, when I turn the encoder quickly, but that is not actually a concern.

// Rotary encoder
#include "RotaryEncoder.h"
#include "YetAnotherPcInt.h"
#define RTRY_ENC_SW A1
#define RTRY_ENC_CLK A2
#define RTRY_ENC_DT A3
RotaryEncoder encoder(RTRY_ENC_CLK, RTRY_ENC_DT);
void onSwitch();
void onRotate();

void setup () {
  pinMode(RTRY_ENC_SW, INPUT_PULLUP);
  PcInt::attachInterrupt(RTRY_ENC_SW, onSwitch, FALLING);
  PcInt::attachInterrupt(RTRY_ENC_CLK, onRotate, CHANGE);
  PcInt::attachInterrupt(RTRY_ENC_DT, onRotate, CHANGE);

  // ...
}

bool hasInputs = false;

void loop () {
  if (hasInputs) {
    hasInputs = false;
    handleButtonInputs();
  }

  // ...
}

void handleButtonInputs() {
  if (switchPressed) {
    switchPressed = false;
    // react ...
  }
  RotaryEncoder::Direction direction = encoder.getDirection();
  if (direction == RotaryEncoder::Direction::CLOCKWISE) {
    // react ...
  } else if (direction == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
    // react ...
  }
}

void onSwitch() {
  hasInputs = true;
  switchPressed = true;
}

void onRotate() {
  hasInputs = true;
  encoder.tick();
}

Hi,
Are you looking for the switch being ON or going from OFF to ON, there is a big difference.

Sounds like you need a faster controller.

Tom.... :smiley: :+1: :coffee: :australia:

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