Rotary encoder interrupts problem

Hello, i have the following library that I was using with Uno R3 and it was working perfect. Now I switch the boards and I am trying to adapt it to Nano 33 IoT but I have a compile error and it looks like an interrupt or macros problem.

#define ENC_RD PIND  //encoder port read
#define A_PIN 2      // pdip 4, associated with INT0 vector; PD2
#define B_PIN 3      // pdip 5, associated with INT1 vector; PD3
#define rstbtn 4     // reset button
volatile long counter = 0;
const float R = 0.15;
float distance = 0;

void setup() {
  Serial.begin(115200);
  pinMode(A_PIN, INPUT_PULLUP);
  pinMode(B_PIN, INPUT_PULLUP);
  pinMode(rstbtn, INPUT_PULLUP);
  attachInterrupt(0, evaluateRotary, CHANGE);
  attachInterrupt(1, evaluateRotary, CHANGE);
}

void resetCounter() {
  noInterrupts();
  counter = 0;
  interrupts();
}

void loop() {

  static long lastCounter = 0;
  distance = R * lastCounter;

  // this use of 'counter' must be protected from interruption:
  noInterrupts();
  lastCounter = counter;
  interrupts();

  Serial.println();
  Serial.print(distance);

  if (digitalRead(rstbtn) == LOW) {
    resetCounter();
  }
}

void evaluateRotary() {
  /* encoder routine. Expects encoder with four state changes between detents */
  /* and both pins open on detent */

  static uint8_t old_AB = 3;                                                                          //lookup table index
  static int8_t encval = 0;                                                                           //encoder value
  static const int8_t enc_states[] PROGMEM = { 0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0 };  //encoder lookup table
  /**/
  old_AB <<= 2;  //remember previous state
  old_AB |= ((ENC_RD >> 2) & 0x03);
  encval += pgm_read_byte(&(enc_states[(old_AB & 0x0f)]));

  if (encval > 3) {  //four steps forward
    counter++;
    encval = 0;
  } else if (encval < -3) {  //four steps backwards
    counter--;
    encval = 0;
  }
}
C:\Users\Bobby\Documents\Arduino\WORKING_ENCODER_CODE!!!\WORKING_ENCODER_CODE!!!.ino: In function 'void evaluateRotary()':
C:\Users\Bobby\Documents\Arduino\WORKING_ENCODER_CODE!!!\WORKING_ENCODER_CODE!!!.ino:1:16: error: 'PIND' was not declared in this scope
 #define ENC_RD PIND  //encoder port read
                ^
C:\Users\Bobby\Documents\Arduino\WORKING_ENCODER_CODE!!!\WORKING_ENCODER_CODE!!!.ino:51:15: note: in expansion of macro 'ENC_RD'
   old_AB |= ((ENC_RD >> 2) & 0x03);
               ^~~~~~
C:\Users\Bobby\Documents\Arduino\WORKING_ENCODER_CODE!!!\WORKING_ENCODER_CODE!!!.ino:1:16: note: suggested alternative: 'SING'
 #define ENC_RD PIND  //encoder port read
                ^
C:\Users\Bobby\Documents\Arduino\WORKING_ENCODER_CODE!!!\WORKING_ENCODER_CODE!!!.ino:51:15: note: in expansion of macro 'ENC_RD'
   old_AB |= ((ENC_RD >> 2) & 0x03);
               ^~~~~~

exit status 1

Compilation error: 'PIND' was not declared in this scope

PIND is a register that exists on the UNO or Nano etc. You don't have the same chip in your Nano 33 IoT and accessing GPIOs does not work in the same way.

I use this encoder library but never tried it on a Nano 33 IoT. (it works with the Teensy LC which uses also a Cortex M0 core as in Nano 33 IoT)

When I change the suggested line from:

#define ENC_RD PIND  //encoder port read

to:

#define SING PIND  //encoder port read

IDE suggests to change the following line:

old_AB |= ((ENC_RD >> 2) & 0x03);

to this:

old_AB |= ((EIC_IRQn >> 2) & 0x03);

I can compile and upload the sketch but the readings in the serial monitor are 0

11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00
11:00:18.449 -> 0.00

The change is only superficial, you still haven't changed the vital thing that is missing and that is the pind register does not exist in your processor.
See this link

again forget it. You don't have the same chip. Those registers do not exist

install the encoder library and try this

#include <Encoder.h>

#define A_PIN 2
#define B_PIN 3
Encoder rotary(A_PIN, B_PIN);

#define rstbtn 4     // reset button
long counter = 0;
const float R = 0.15;
float distance = 0;

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

void loop() {
  static long lastCounter = 0;
  long currentCounter = rotary.read();

  if (lastCounter != currentCounter) {
    distance = R * lastCounter;
    Serial.print("distance: ");
    Serial.println(distance);
    lastCounter = currentCounter;
  }

  if (digitalRead(rstbtn) == LOW) rotary.write(0);
}

assuming you are really using pins 2,3 and 4

you'll probably find that your encoder provides 4 ticks per click, it's just a matter of dividing the read() by 4 in that case (you can use >> 2 to do so)

The code now works, but it constantly adds the next value after I return it to 0....
if say i rotate it from 0 to 20 and back to 0 it shows 20. Then when I rotate it again from 0 to 10 it adds the last value to the previous and the final print in the serial monitor is 30 and so on, the number constantly grows.

right - I typed it here so did not test it at all.

forgot that when you reset the encoder, you also need to set lastCounter to 0

#include <Encoder.h>

#define A_PIN 2
#define B_PIN 3
Encoder rotary(A_PIN, B_PIN);

#define rstbtn 4     // reset button
long counter = 0;
const float R = 0.15;
float distance = 0;

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

void loop() {
  static long lastCounter = 0;
  long currentCounter = rotary.read();

  if (lastCounter != currentCounter) {
    distance = R * currentCounter;
    Serial.print("distance: ");
    Serial.println(distance);
    lastCounter = currentCounter;
  }

  if (digitalRead(rstbtn) == LOW) {
    rotary.write(0);
    lastCounter = 0;
  }
}

and of course the distance needs to be calculated with the currentCounter

I think I didn't explain myself. The reset button is working, when I press the button it zeroes the value and the encoder starts counting from zero again.
The problem is when the encoder is returned to its physical zero position the reading on the display doesn't go to zero but stays in the last encoder position.

and when the encoder is rotated again from its physical zero position the reading adds this new value to the previous. And even though the encoder is back in its physical position the value is even bigger cos it was a sum of all previous encoder movements.
Hopefully, that makes sense.

can you try that code, turn the knob until you see 10 and go back to 0 and show us (with code tags) what you get in the Serial monitor

#include <Encoder.h>

#define A_PIN 2
#define B_PIN 3
Encoder rotary(A_PIN, B_PIN);

#define rstbtn 4     // reset button
long counter = 0;

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

void loop() {
  static long lastCounter = 0;
  long currentCounter = rotary.read() >> 2;

  if (lastCounter != currentCounter) {
    Serial.print("currentCounter: ");
    Serial.println(currentCounter);
    lastCounter = currentCounter;
  }

  if (digitalRead(rstbtn) == LOW) {
    rotary.write(0);
    lastCounter = 0;
  }
}

same situation: from zero to 10 and back to 0 and back out to about 5. But it still kept counting.

12:11:29.184 -> currentCounter: 1
12:11:29.217 -> currentCounter: 2
12:11:29.217 -> currentCounter: 3
12:11:29.260 -> currentCounter: 2
12:11:29.260 -> currentCounter: 3
12:11:29.260 -> currentCounter: 2
12:11:29.260 -> currentCounter: 3
12:11:29.296 -> currentCounter: 2
12:11:29.296 -> currentCounter: 3
12:11:29.296 -> currentCounter: 2
12:11:29.296 -> currentCounter: 3
12:11:29.482 -> currentCounter: 4
12:11:29.482 -> currentCounter: 5
12:11:29.514 -> currentCounter: 6
12:11:29.514 -> currentCounter: 7
12:11:29.546 -> currentCounter: 6
12:11:29.546 -> currentCounter: 7
12:11:29.546 -> currentCounter: 6
12:11:29.580 -> currentCounter: 7
12:11:29.580 -> currentCounter: 6
12:11:29.580 -> currentCounter: 7
12:11:29.622 -> currentCounter: 6
12:11:29.622 -> currentCounter: 7
12:11:29.744 -> currentCounter: 8
12:11:29.744 -> currentCounter: 9
12:11:29.776 -> currentCounter: 10
12:11:29.776 -> currentCounter: 9
12:11:29.776 -> currentCounter: 10
12:11:29.808 -> currentCounter: 9
12:11:29.808 -> currentCounter: 10
12:11:29.808 -> currentCounter: 9
12:11:29.808 -> currentCounter: 10
12:11:29.934 -> currentCounter: 11
12:11:29.934 -> currentCounter: 12
12:11:29.966 -> currentCounter: 11
12:11:29.966 -> currentCounter: 12
12:11:29.966 -> currentCounter: 11
12:11:30.012 -> currentCounter: 12
12:11:30.081 -> currentCounter: 13
12:11:30.081 -> currentCounter: 14
12:11:30.119 -> currentCounter: 13
12:11:30.119 -> currentCounter: 14
12:11:30.119 -> currentCounter: 13
12:11:30.119 -> currentCounter: 14
12:11:30.201 -> currentCounter: 13
12:11:30.201 -> currentCounter: 14
12:11:30.242 -> currentCounter: 13
12:11:30.242 -> currentCounter: 14
12:11:30.281 -> currentCounter: 15

Can you switch pin 2 and 3 ?

Done the pin swap on the board, still the same. All day today I am trying different libraries and all of them are working except that the value constantly grows .........just multiplies itself every time you rotate the encoder. It doesn't register the reverse direction only adds the forward direction. Very strange.

I don't have that board so can't try... may be the library is not compatible

the expected behavior would be like in this simulation

What if you do not use interrupts. Does the encoder behave as expected?

#define ENCODER_DO_NOT_USE_INTERRUPTS
#include <Encoder.h>

this is the bit of the code that does what I want but I can't get my head around how to do the internal pinD processes equivalent in this new processor.

void evaluateRotary() {
  /* encoder routine. Expects encoder with four state changes between detents */
  /* and both pins open on detent */

  static uint8_t old_AB = 3;                                                                          //lookup table index
  static int8_t encval = 0;                                                                           //encoder value
  static const int8_t enc_states[] PROGMEM = { 0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0 };  //encoder lookup table
  /**/
  old_AB <<= 2;  //remember previous state
  old_AB |= ((ENC_RD >> 2) & 0x03);
  encval += pgm_read_byte(&(enc_states[(old_AB & 0x0f)]));

  if (encval > 3) {  //four steps forward
    counter++;
    encval = 0;
  } else if (encval < -3) {  //four steps backwards
    counter--;
    encval = 0;
  }
}

use digitalRead() on pin A and B and combine them into the 2 LSb of old_AB

can you help me here? How is that going to look in the code?

something like this

void evaluateRotary() {
  /* encoder routine. Expects encoder with four state changes between detents */
  /* and both pins open on detent */

  static uint8_t old_AB = 3;                                                                          //lookup table index
  static int8_t encval = 0;                                                                           //encoder value
  static const int8_t enc_states[] PROGMEM = { 0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0 };  //encoder lookup table
  /**/
  old_AB <<= 2;  //remember previous state
  uint8_t pin2Val = digitalRead(A_PIN) == HIGH ? 1 : 0;
  uint8_t pin3Val = digitalRead(B_PIN) == HIGH ? 1 : 0;
  uint8_t combined = (pin3Val << 1) | pin2Val;
  old_AB |= combined;
  encval += pgm_read_byte(&(enc_states[(old_AB & 0x0f)]));

  if (encval > 3) {  //four steps forward
    counter++;
    encval = 0;
  } else if (encval < -3) {  //four steps backwards
    counter--;
    encval = 0;
  }
}

Basically I made every step explicit:

  uint8_t pin2Val = digitalRead(A_PIN) == HIGH ? 1 : 0;
  uint8_t pin3Val = digitalRead(B_PIN) == HIGH ? 1 : 0;
  uint8_t combined = (pin3Val << 1) | pin2Val;
  old_AB |= combined;

which is similar (but much slower and non atomic) to what this line of code was doing

  old_AB |= ((ENC_RD >> 2) & 0x03);

Annoyingly it doesn't work.