MCP23017 "freezes" up

Hello all,

I have a problem with addind more I/O ports using MCP23017 to Arduino.

My setup is:

I have followed the instructions from Adding Digital I/O To Your Arduino: Part 3 - The MCP23017 - Woolsey Workshop. Everyithing works fine for a short time (typically 30 - 60 secs), but after that MCP23017 seems to "freeze" so it just just stop working. After taking off power and putting it on the same thing happens.

What I have done so far:

  • Checked, double- and triple-checked my wiring
  • Tried with 3 different MCP23017-E/SP chips
  • Tried with different Arduino libraries
  • Simplified the code so it just uses the output ports

Now, since the Arduino built-in led keeps blinking but extended LED's stop to work after a while the problem should be not on Arduino's side.

Any experiences/advices?

Here's my simplified code:

#include <MCP23017.h>

#define MCP23017_I2C_ADDRESS 0x20  // I2C address of the MCP23017 IC
MCP23017 mcp23017 = MCP23017(MCP23017_I2C_ADDRESS);  // instance of the connected MCP23017 IC

const uint8_t LED0 = 0;      // GPA0 (21) of the MCP23017
const uint8_t LED1 = 1;      // GPA1 (22) of the MCP23017
const uint8_t LED2 = 2;      // GPA2 (23) of the MCP23017
const uint8_t LED3 = 3;      // GPA3 (24) of the MCP23017
const uint8_t LED4 = 4;      // GPA4 (25) of the MCP23017
const uint8_t LED5 = 5;      // GPA5 (26) of the MCP23017
const uint8_t LED6 = 6;      // GPA6 (27) of the MCP23017
const uint8_t LED7 = 7;      // GPA7 (28) of the MCP23017

void setup() {
   Wire.begin();     // initialize I2C serial bus
   mcp23017.init();  // initialize MCP23017 IC

   // Configure MCP23017 I/O pins
   configurePinsWithPinMode(); 

   // Reset MCP23017 ports
   mcp23017.writeRegister(MCP23017Register::GPIO_A, 0x00);
   mcp23017.writeRegister(MCP23017Register::GPIO_B, 0x00);

   Serial.begin(9600);
   Serial.println("Program started!");

   pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
   testLeds();
   delay(5000);
}

void configurePinsWithPinMode() {
   // Configure output pins
   mcp23017.pinMode(LED0, OUTPUT);
   mcp23017.pinMode(LED1, OUTPUT);
   mcp23017.pinMode(LED2, OUTPUT);
   mcp23017.pinMode(LED3, OUTPUT);
   mcp23017.pinMode(LED4, OUTPUT);
   mcp23017.pinMode(LED5, OUTPUT);
   mcp23017.pinMode(LED6, OUTPUT);
   mcp23017.pinMode(LED7, OUTPUT);
}

void testLeds() {
  blinkLed(LED0);
  blinkLed(LED1);
  blinkLed(LED2);
  blinkLed(LED3);
  blinkLed(LED4);
  blinkLed(LED5);
  blinkLed(LED6);
  blinkLed(LED7); 
}

void blinkLed(uint8_t ledNo) {
    Serial.println("Blinking LED: " + String(ledNo));
    for(int i=0; i<10; i++) {
      setLedStatus(ledNo, true);
      delay(200);
      setLedStatus(ledNo, false);
      delay(200);     
    }
}

void setLedStatus(uint8_t ledNo, bool ledStatus) {
   mcp23017.digitalWrite(ledNo, ledStatus);

   // debug
   digitalWrite(LED_BUILTIN, ledStatus);   // turn the LED on (HIGH is the voltage level)
}

x
please

  • post a schematic
  • and post pictures of your setup where we can see each single component (power source, arduino, IC, LEDs ... ) and each single wire.

edit:
after posting a picture the mistake was immediately spotted by helpers ... what surprise ...

Just a question.

You configure the pin modes then reset the registers. Is that correct?

I have no knowledge of the MCP23017 library since I directly address the pins/ports, It is an easy enough chip to address directly.

NB. You do have pull-up resisters on the I2C lines?

That is correct. However, I tried also swap the order of them but the result was same

That's interesting. Do you have any exampels/tutorial links how to do that?

Definately I have

@kjkallela Have a look at the following sketch I put together for you. It will toggle a LED attached to pin 21 of the MCP.

/* Sample demo using MCP23017 
 * Toggle LED connected to a GPIOA pin. 
 */
 
#include <Wire.h>

// Define MPC Port addresses
// NOTE: The following register addresses assume IOCON.BANK = 0 (default)
#define IODIRA   0x00   // IO direction  (0 = output, 1 = input (Default))
#define IODIRB   0x01
#define GPPUA    0x0C   // Pull-up resistor (0 = disabled, 1 = enabled)
#define GPPUB    0x0D
#define GPIOA    0x12   // Port value. Write to change, read to obtain value
#define GPIOB    0x13

int8_t MPC_Address = 0x20;

///////////////////////////////////////////////////////////////////////////////
//
void setup() {
  // put your setup code here, to run once:
  Wire.begin();

  // Setup MCP23017 Ports - PortA all outputs
  writeToMPC(IODIRA, 0x00);               // Set MCP23017 Port A as all Output (0=output; 1=input)

  // ... - PortB all inputs with pull-up
  writeToMPC(IODIRB, 0xFF);               // Set MCP23017 Port B as all Input (0=output; 1=input)
  writeToMPC(GPPUB, 0xFF);                // Enable internal pullup for Port A inputs

}

///////////////////////////////////////////////////////////////////////////////
//
void loop() {
  // put your main code here, to run repeatedly:
  static int8_t lastStatus = LOW;
  
  // Any GPIOB pin pulled low will toggle the LED
  uint8_t status = readButton();
  if ( status ) {
    lastStatus = !lastStatus;
    writeToMPC(GPIOA,lastStatus ? 0x01 : 0x00);   // LED connected to GPA0 (MPC pin 21)
    while ( readButton() );
  }
}

///////////////////////////////////////////////////////////////////////////////
//
uint8_t readButton() {
  /* All the port pins will be default high due to the pullup setting
   * therefor we need to bit invert the reading to get a 0 (no pin selected).
   * when no pins pulled low.
   */
  uint8_t status = ~readMPCbyte(GPIOB);
  return status;
}  

///////////////////////////////////////////////////////////////////////////////
//
uint8_t readMPCbyte(uint8_t port) {
  Wire.beginTransmission(MPC_Address);
  Wire.write(port);                 // set MCP23017 port register
  Wire.endTransmission();
  Wire.requestFrom(MPC_Address, 1); // request one byte of data from port
  return Wire.read();               // read and return the requested byte
}

///////////////////////////////////////////////////////////////////////////////
//
void writeToMPC(uint8_t port, uint8_t value)  {
  Wire.beginTransmission(MPC_Address);
  Wire.write(port);         // Set required port
  Wire.write(value);        // Load value into port
  Wire.endTransmission();
}

Tested on an UNO.

Thank you @Willem43, that was a really nice short code which worked fine. However, it did not solve my original problem. If you change the code a bit like below, you will probable see what happens. Leave it running, grab a cup of coffee or do something else meanwile the led is blinking. During 15 minutes or so it stops working :thinking:

void setup() {
  // put your setup code here, to run once:
  Wire.begin();

  // Setup MCP23017 Ports - PortA all outputs
  writeToMPC(IODIRA, 0x00);               // Set MCP23017 Port A as all Output (0=output; 1=input)

  // ... - PortB all inputs with pull-up
  writeToMPC(IODIRB, 0xFF);               // Set MCP23017 Port B as all Input (0=output; 1=input)
  writeToMPC(GPPUB, 0xFF);                // Enable internal pullup for Port A inputs

  pinMode(LED_BUILTIN, OUTPUT);


}

///////////////////////////////////////////////////////////////////////////////
//
void loop() {
  // put your main code here, to run repeatedly:
  
  // Any GPIOB pin pulled low will toggle the LED
    lastStatus = !lastStatus;
    writeToMPC(GPIOA,lastStatus ? 0x01 : 0x00);   // LED connected to GPA0 (MPC pin 21)

    digitalWrite(LED_BUILTIN, lastStatus);   // turn the LED on (HIGH is the voltage level)    
    delay(5000);
}

Here's the connection:

Hi, @kjkallela

Have you got 4k7 pullup resistors on the two I2C lines?
I can't see any on your image.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, component names and pin labels.

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

@kjkallela I do not see your MCP RESET (pin 18) being pulled high. My setup without the 4K7 pullup resistors also works but should have them.

I have my system, with your changes, running for 20 minutes and still working fine.

Thank you Willem43 and TomGeorge ! I wired the pin 18 high and added pull-up resistors and now the led has been blinking over an hour.

Glad it is working.

I am sure your problem was the floating reset. I have been through that before.

I had one chip that would not work at all, thought it was faulty. The next worked unreliably, a bit like yours. I then clicked that the reset needs to be pulled high. No problems with either after that.

Hello gentlemen, this was very informative! Saved my butt after i had forgotten about the I2C pullup resistors. One MCP happened to work just fine, but after wiring 3 of them, things really became unstable, even with all their reset pins pulled up high.
With the pullup resistors it now works fast and stable :star_struck:

Lesson learned, thanks again!

The pullup resistors are required because the outputs onnthose pins are OPEN COLLECTOR, meaning they act like a switch to GND that needs a resistor to pull it high because without it, there is no voltage to switch to GND.

Hi @vagos21

Was wondering if the pull-up resistor needs to be 4k7 or 3k3 ( if 3.3V) or it can be anything above 2k for the SDA/SDL pins ?

Thanks,
watersoup

The standard value used for most hobbyist applications is 4.7k. If you are asking the question you are asking, more than likely you have not been using electronics long enough to realize that there are rules that should be followed. More than likely you simply don't happen to have a 4.7k resistor and wanted to know if the value is critical. The answer is yes and no. As long as the value you use is at least withing 25% of 4.7k you'll probably alright. You would not, for example , want to use 1k.
The actual value is a function of how many slave devices are on the bus and the wire length. The transmission speed is also a factor , since there is more than one speed available in I2C. I have never used anything but 4.7k but if you want to test your luck I suppose you can try 3.3k. If you want to read the rules you could read
this . I would guess 3.3k is ok.