Problems using Adafruit MCP23017 I2C Expander with Arduino Uno

Hello,

I feel like I have to be missing something very simple here. I have connected the Adafruit MCP23017 expander board using the Qwiic connector to my Uno, and using an I2C scanning sketch I can see that it is properly recognized at the default address of 0x20.

I am trying to use a photo interrupt module but cannot get it to work while I have no problems using it directly connected to the Uno's GPIO pins.

As long as the non-transparent object passes through the slot type, it can be triggered to output TTL low level.

Working Sketch while connected directly to the Uno:

#include <Arduino.h>

#define OPTICAL_PIN 2

bool lastOpticalState = HIGH; // Assume the beam starts unblocked

void setup() {
  Serial.begin(115200);
  pinMode(OPTICAL_PIN, INPUT_PULLUP);
  lastOpticalState = digitalRead(OPTICAL_PIN);
}

void loop() {
  bool currentOpticalState = digitalRead(OPTICAL_PIN);

  if (lastOpticalState != currentOpticalState) {
    if (currentOpticalState == LOW) {
      Serial.println("Beam BLOCKED");
    } else {
      Serial.println("Beam UNBLOCKED");
    }
    lastOpticalState = currentOpticalState;
  }

  delay(10);
}

This works as expected, when the sensor is blocked, serial prints out "Beam BLOCKED" and when I remove the block, it prints out "Beam UNBLOCKED", so exactly what I am looking for.

When I modified the code to use the Adafruit_MCP23X17 library, connect the module to the expander board (and VCC to the Uno's 5v pin) I get no output:

#include <Arduino.h>
#include <Adafruit_MCP23X17.h>

#define OPTICAL_PIN 0

Adafruit_MCP23X17 mcp;
bool lastOpticalState = HIGH; // Assume the beam starts unblocked

void setup() {
  Serial.begin(115200);
  mcp.begin_I2C();
  mcp.pinMode(OPTICAL_PIN, INPUT_PULLUP);
  lastOpticalState = mcp.digitalRead(OPTICAL_PIN);
}

void loop() {
  bool currentOpticalState = mcp.digitalRead(OPTICAL_PIN);

  if (lastOpticalState != currentOpticalState) {
    if (currentOpticalState == LOW) {
      Serial.println("Beam BLOCKED");
    } else {
      Serial.println("Beam UNBLOCKED");
    }
    lastOpticalState = currentOpticalState;
  }

  delay(10);
}

Trying to get down to what could be the issue, I tried the mcp23xx_button example:

#include <Adafruit_MCP23X17.h>

#define BUTTON_PIN 1  // MCP23XXX pin button is attached to

Adafruit_MCP23X17 mcp;

void setup() {
  Serial.begin(9600);
  //while (!Serial);
  Serial.println("MCP23xxx Button Test!");

  // uncomment appropriate mcp.begin
  if (!mcp.begin_I2C()) {
  //if (!mcp.begin_SPI(CS_PIN)) {
    Serial.println("Error.");
    while (1);
  }

  // configure pin for input with pull up
  mcp.pinMode(BUTTON_PIN, INPUT_PULLUP);

  Serial.println("Looping...");
}

void loop() {
  // LOW = pressed, HIGH = not pressed
  if (!mcp.digitalRead(BUTTON_PIN)) {
    Serial.println("Button Pressed!");
    delay(250);
  }
}

but I only ever get

02:20:40.177 -> MCP23xxx Button Test!
02:20:42.196 -> Error.

Which I do not understand why even this is not working, leading me to think that I am misunderstanding something very simple. I feel like I've read every post in the last decade about the MCP23017 but lack the programming knowledge to understand interfacing with it directly. The simple button sketch not working drove me to make my own post in hopes that I can get to the bottom of this.

Thank you

Could you show the details of your wiring please?

For example, you mention that you have connected VCC to 5V on the Uno, but didn't mention whether you had connected GND to GND on the UNO?

In the second example it does seem as though the mcp initialisation is failing for some reason so you need to identify the cause. Is it still being detected by the I2C scanner program?

Are you sure you have that connector wired correcty?
Which Uno R3 or R4?

Yes, the Adafruit MCP23017 expander board has header pins for GPIO and GND; the STEMMA QT/ Qwiic cable provides GND, VCC, SDA and SCL to the breakout board. So I only needed to supply power independently to the photo interrupter. The photo interrupter has a small LED that indicates whether or not the board is reporting HIGH or LOW so I know that it is working properly. The breakout board also has a PWR LED so I know that it is receiving power and the Uno is detecting it on the I2C bus.

Yes, I worked backwards from the mcp implementation of the photo interrupter not working, so I've run the I2C scanning multiple times. The breakout board is communicating with the Uno at the default address for the Adafruit MCP23017 expander - 0x20. If I explicitly declare this in the code with mcp.begin(0x20); there is no change in behavior, neither in my code nor the button example.

I would, but Fritzing doesn't have the Uno R4, Adafruit MCP23017 expander board, or the photo interrupt module. Happy to make one if you can point me to a good site/program.

Regardless, for testing the button code that is even more stripped down, it really is as simple a set up as it sounds - 1 usb-c cable to the Uno R4, 1 STEMMA QT cable to the breakout board, and if I ever got past the initialization error I was just going to use a wire to short the GPIO pin to ground for the "button". I have tried 2 different Uno R4s, 3 different combinations of usb cables/ports, 2 different adafruit breakouts, and 2 different STEMMA cables to try to isolate this to code.

Official Uno R4 WIFI, I should've specified.

Yes, I am sure. I did not make the cable myself, I bought official cables and the wiring matches the spec. I've also tried 2 different Uno R4s, 3 different combinations of usb cables/ports, 2 different adafruit breakouts, and 2 different STEMMA cables to try to isolate this to the code (again unless I am missing something really simple).

The R4 has two I2C busses. The Qwiic connector uses the second I2C bus.
So you need to use:
mcp.begin_I2C (address, &Wire1); where address =0x20

NOTE: The Qwiic I2C bus is 3.3V only, Vcc will be 3.3V

You will probably want to use the regular I2C bus on the SCL, SDA pins.

Ah! That was the missing piece thank you so much! I knew I had to be missing something simple. Would you believe that I knew going into this that the Uno R4 used Wire1 to communicate over the Qwiic port? I even included it in my I2C scanner sketch. I just didn't connect the dots that I needed to declare that in the mcp.begin(). I double checked the docs and realized I only read the Adafruit_MCP23X17.cpp not also the Adafruit_MCP23XXX.cpp src, where it says, clear as day

bool Adafruit_MCP23XXX::begin_I2C(uint8_t i2c_addr, TwoWire *wire) {
  i2c_dev = new Adafruit_I2CDevice(i2c_addr, wire);
  return i2c_dev->begin();
}

Using mcp.begin_I2C(0x20, &Wire1); solves all of my issues thank you so much!

Have a nice day!