Two encoders with interrupt using MCP23017 module

Hello, I'm new to arduino and to code...

I've been trying to figure out how to use interrupt encoders using an MCP23017 module on Arduino pro micro (clone) for several days without success.

I tested my circuit with the example code from Adafruit_MCP23017_Arduino_Library (mcp23xxx_interrupt) and it works.

But when I use a code I've found on internet, it doesn't work. Nothing happens in the serial.
It's supposed to be a debugging code so it should work and I must have a problem in my setup...
Here is the original code:

And more, after uploading the code, my ardiuno pro micro is blocked. I have to disconnect the SDA and SCL connections and re-upload an empty code to get it back.

Here's my schematic:

here are the codes I use:

-mcp23xxx_interrupt, it works : mcp23xxx_interrupt - Pastebin.com

-Encoders_interrupt (Adapted from the link above), does not work:

/*
  Made by Gustavo Silveira, 2023.

  http://www.musiconerd.com
  http://www.youtube.com/musiconerd
  http://facebook.com/musiconerdmusiconerd
  http://instagram.com/musiconerd/
  http://www.gustavosilveira.net
  gustavosilveira@musiconerd.com

*/

//#include <Wire.h>

#include <Adafruit_MCP23X17.h> // Include the Adafruit_MCP23X17 library
Adafruit_MCP23X17 mcp; // Create an instance of the Adafruit_MCP23X17 class

const int I2C_ADDRESS = 0x20; // MCP23017 I2C address

int INT_PIN = 7;
byte encIndex;

const int ENCODER_N = 2; // Number of encoders used
int encoderPin[ENCODER_N][2] = {{0, 1}, {2, 3}}; // Pin numbers for the A and B channels of each encoder
// const int encoderPin[ENCODER_N][2] = {{8, 9}, {0, 1}, {12, 13}, {10, 11}, {2, 3}, {14, 15}}; // Pin numbers for the A and B channels of each encoder

int count[ENCODER_N] = {0}; // Current count of each encoder
int lastCount[ENCODER_N] = {0}; // Previous count of each encoder

int encoderA[ENCODER_N] = {0}; // Current state of the A channel of each encoder
int encoderB[ENCODER_N] = {0}; // Current state of the B channel of each encoder
int lastEncoderA[ENCODER_N] = {HIGH}; // Previous state of the A channel of each encoder
int lastEncoderB[ENCODER_N] = {HIGH}; // Previous state of the B channel of each encoder

void setup() {
  
  Serial.begin(9600);

  while (!Serial) {
    Serial.println("waiting...");
  }
  Serial.println();

  // uncomment appropriate mcp.begin
  if (!mcp.begin_I2C(I2C_ADDRESS, &Wire)) { // Wire1 or Wire
    //if (!mcp.begin_SPI(CS_PIN)) {
    Serial.println("MCP23017 Error.");
    while (1)
      ;
  } else {
    Serial.println("MCP23017 Success.");
  }

  // configure MCU pin that will read INTA/B state
  pinMode(INT_PIN, INPUT_PULLUP);

  for (int i = 0; i < ENCODER_N; i++) {
    for (int j = 0; j < 2; j++) {
      mcp.pinMode(encoderPin[i][j], INPUT_PULLUP);
      mcp.setupInterruptPin(encoderPin[i][j], CHANGE);
    }
  }

  // OPTIONAL - call this to override defaults
  // mirror INTA/B so only one wire required
  // active drive so INTA/B will not be floating
  // INTA/B will be signaled with a LOW
  mcp.setupInterrupts(true, false, CHANGE);

  //attachInterrupt(digitalPinToInterrupt(INT_PIN), getLastInterrupt, CHANGE); // gets the MCP23017 pin that was "interrupted"
  attachInterrupt(digitalPinToInterrupt(INT_PIN), readEncoder, CHANGE); // reads the encoder in the MCP23017
  mcp.clearInterrupts();  // clearInterrupts

  //Serial.println("oi...");
}

void loop() {


}

void readEncoder() {

  byte interruptPin = mcp.getLastInterruptPin();


  if (interruptPin >= 0 && interruptPin <= 16) {

    //    Serial.print("interruptPin: ");
    //    Serial.println(interruptPin);

    updateEncIndex(interruptPin);

    //    Serial.print("encIndex: ");
    //    Serial.println(encIndex);

    int encoderA = mcp.digitalRead(encoderPin[encIndex][0]); // Read the state of the A channel of the current encoder
    int encoderB = mcp.digitalRead(encoderPin[encIndex][1]); // Read the state of the B channel of the current encoder

    if (encoderA != lastEncoderA[encIndex]) { // Check if the state of the A channel has changed
      if (encoderA == LOW) { // If the state of the A channel is LOW
        if (encoderB == LOW) { // If the state of the B channel is also LOW
          count[encIndex]--; // Decrement the count for this encoder
        } else {
          count[encIndex]++; // Otherwise, increment the count for this encoder
        }
      }
      lastEncoderA[encIndex] = encoderA; // Update the previous state of the A channel
    }

    if (count[encIndex] != lastCount[encIndex]) { // If the count has changed
      Serial.print("Encoder ["); // Print the encoder number
      Serial.print(encIndex);
      Serial.print("]: ");
      Serial.println(count[encIndex]); // Print the current count
      lastCount[encIndex] = count[encIndex]; // Update the previous count
    }


  }


  mcp.clearInterrupts();  // clear

}



void updateEncIndex(int _interruptPin) {

  // Loop through each subarray in encoderPin
  for (int i = 0; i < ENCODER_N; i++) {
    // Check if _interruptPin matches either value in the current subarray
    if (_interruptPin == encoderPin[i][0] || _interruptPin == encoderPin[i][1]) {
      // If a match is found, set encIndex to the current index i and exit the loop
      encIndex = i;
      break;
    }
  }
}


void getLastInterrupt() {


  byte interruptPin = mcp.getLastInterruptPin();

  if (interruptPin >= 0 && interruptPin < 16) {

    Serial.println(interruptPin);
    //Serial.println(Serial.print(interruptPin));

    //  Serial.print("Interrupt detected on pin: ");
    //  Serial.println(interruptPin);
    //  Serial.print("Pin states at time of interrupt: 0b");
    //  Serial.println(mcp.getCapturedInterrupt(), 2);
    delay(25);  // debounce

    // NOTE: If using DEFVAL, INT clears only if interrupt
    // condition does not exist.
    // See Fig 1-7 in datasheet.

  }
  mcp.clearInterrupts();  // clear
}

Thanks for your help!

Can you also include the output you see on the console when this application runs.

thanks for your interest,

I've inserted "tests" in the code in the form of serial.print ("test1"), serial.print ("test2"), serial.print ("test3") in different parts of the code. only the first one appears on the console.

void setup() {
  
  Serial.begin(9600);

  while (!Serial) {
    Serial.println("waiting...");
  }
  Serial.println();

  // uncomment appropriate mcp.begin
  if (!mcp.begin_I2C(I2C_ADDRESS, &Wire)) { // Wire1 or Wire
    //if (!mcp.begin_SPI(CS_PIN)) {
    Serial.println("MCP23017 Error.");
    while (1)
      ;
  } else {
    Serial.println("MCP23017 Success.");
  }

  // configure MCU pin that will read INTA/B state
  pinMode(INT_PIN, INPUT_PULLUP);

  for (int i = 0; i < ENCODER_N; i++) {
    for (int j = 0; j < 2; j++) {
      mcp.pinMode(encoderPin[i][j], INPUT_PULLUP);
      mcp.setupInterruptPin(encoderPin[i][j], CHANGE);
    }
  }
  Serial.print("test1");
  // OPTIONAL - call this to override defaults
  // mirror INTA/B so only one wire required
  // active drive so INTA/B will not be floating
  // INTA/B will be signaled with a LOW
  mcp.setupInterrupts(true, false, CHANGE);

  Serial.print("test2");

  //attachInterrupt(digitalPinToInterrupt(INT_PIN), getLastInterrupt, CHANGE); // gets the MCP23017 pin that was "interrupted"
  attachInterrupt(digitalPinToInterrupt(INT_PIN), readEncoder, CHANGE); // reads the encoder in the MCP23017
  mcp.clearInterrupts();  // clearInterrupts

  //Serial.println("oi...");

  Serial.print("test3");
}

here is what i see on the console:

waiting...

MCP23017 Success.

test1

here is a video of the micro pro getting stuck after uploading the code.
A bit long, sorry!
I got an error at the end because I didn't rewire before uploading the code...

You cannot do I2C communication in an AVR interrupt handler, the Wire library does not support this.

1 Like

okay, thanks Pieter.

But why is there this mcp23xxx_interrupt example in the Adafruit_MCP23017 library?
isn't that what it's for?

And the Wire library is not enabled in the code...

Do you understand the example properly?
It doesn't use an attachInterrupt() function on the arduino itself, all interrupts uses only on MCP23017 chip.

Wire library is enabled inside the Adafruit_MCP23017 library. It used to interface the MCP23017.
See your code:

Thank you b707 for your answer, I'm definitely a beginner and I do not fully understand the code. It's clearer with your help.

What I find strange is that this code works in a video I saw (on a private platform) by the person who wrote the code... but he's using a Teensy (LC, I think) and not a pro micro. maybe that's why?

My final project is to play with an MPR121 capacitive touch sensor with SSD1306 oled screen and two encoders to control sensor sensitivity and display it on the screen. But without interrupts inside the code it doesn't work well. I'm using the Adafruit_SSD1306 library for the screen code and Adafruit_MPR121 library for the touch sensor.

Do you have any advice on how to do it differently?

thank you very much!

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