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)
}
@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();
}
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
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);
}
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.
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
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.
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.