Posted on Leave a comment

Add 32 Buttons With Just 2 Pins With MCP23017 Expander

I need about 40 buttons for my synth project.  I’m trying to run the entire synth with a single STM32 Blue Pill.  I’ve already shown how I can run 256 LEDs with 3 pins LINK IT HERE.  Since the MCP23017 is I2C, I could possibly run 128 buttons on 2 pins.  For now, I’m just running two of my Tactile Switch Female Dog boards (each with 16 buttons and a MCP23017 chip).

Stuff I Didn’t Know Yesterday About The MCP23017 Expander

  • I needed to power cycle this chip quite a bit when it didn’t act as expected.  If you start to lose your mind, go ahead and lose it.  Then kill the power for a second.
  • When using the internal pullups, we are in active low land.  Pushing the button gives a zero.  That’s normal.  I got confused when using the onboard STM32 Blue Pill LED as the stupid thing is also active low.  Apparently, I can handle only one active low thing per week.

I debated whether I wanted to use a library or just bang it out with Wire commands.  It’s really all the same, but I guess the library saves 2 seconds of work (at what cost?).  I opted for the Adafruit MCP23017 library, but I’m keeping a link close by for the tutorial using Wire so I don’t have to think or open a datasheet today.

Code For A Single MCP23017 Expander

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_MCP23017.h>
 
#define ONBOARD_LED PC13
 
// Connect pin #12 of the expander to PB6  I2C clock
// Connect pin #13 of the expander to PB7  I2C data
 
Adafruit_MCP23017 mcp;
 
void setup()
{
    // The address is specified using the A0, A1, and A2 pins
    mcp.begin(); // use default address 0, otherwise, the address needs to be defined
 
    for (int i = 0; i < 16; i++)
    {
        mcp.pinMode(i, INPUT); // MCP23017 pins are set for inputs
        mcp.pullUp(i, HIGH);   // turn on a 100K pullup internally
        delay(10);
    }
    pinMode(ONBOARD_LED, OUTPUT); // use the c13 LED as debugging
 
    // Sanity check to confirm functionality with onboard LED
    for (int i = 0; i < 8; i++)
    {
        digitalWrite(ONBOARD_LED, HIGH);
        delay(100);
        digitalWrite(ONBOARD_LED, LOW);
        delay(100);
    }
}
 
void loop()
{
 
    // Check for a button press of any button.  A LOW indicates a button press
    if (mcp.digitalRead(0) == 0 ||
        mcp.digitalRead(1) == 0 ||
        mcp.digitalRead(2) == 0 ||
        mcp.digitalRead(3) == 0 ||
        mcp.digitalRead(4) == 0 ||
        mcp.digitalRead(5) == 0 ||
        mcp.digitalRead(6) == 0 ||
        mcp.digitalRead(7) == 0 ||
        mcp.digitalRead(8) == 0 ||
        mcp.digitalRead(9) == 0 ||
        mcp.digitalRead(10) == 0 ||
        mcp.digitalRead(11) == 0 ||
        mcp.digitalRead(12) == 0 ||
        mcp.digitalRead(13) == 0 ||
        mcp.digitalRead(14) == 0 ||
        mcp.digitalRead(15) == 0)
    {
        digitalWrite(ONBOARD_LED, LOW); // Turn on the dumb, active low PC13 onboard LED
        delay(1000);                    // Keep the LED on a second.
    }
    else
    {
        digitalWrite(ONBOARD_LED, HIGH); // Turn off the dumb, active low PC13 onboard LED
    }
}

Code For 2 Simultaneous MCP23017 Expanders (32 Buttons)

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_MCP23017.h> 

#define ONBOARD_LED PC13

/********** HARDWARE *****************/
// Connect pin #12 of the expander to PB6  I2C clock
// Connect pin #13 of the expander to PB7  I2C data
// Remember that I2C requires pullups on clock and data pins.
// I used 2.2k resistors because I mindlessly read it on the internet
// From a guy who typed wth confidence

/********** NOTES ********************/
// If the board acts stupid and you don't want to blame yourself,
// power cycle your microcontroller and the MCP23017

Adafruit_MCP23017 mcp1; // Create an object for the first board.
Adafruit_MCP23017 mcp2; // Create an object for the second board.

void setup()
{
    // The address is specified using the A0, A1, and A2 pins
    mcp1.begin();    // use default address 0, otherwise, the address needs to be defined
    mcp2.begin(001); // make sure to give A0 5V.

    for (int i = 0; i < 16; i++)
    {
        mcp1.pinMode(i, INPUT); // MCP23017 #1 pins are set for inputs
        mcp1.pullUp(i, HIGH);   // turn on a 100K pullup internally
        mcp2.pinMode(i, INPUT); // MCP23017 #2 pins are set for inputs
        mcp2.pullUp(i, HIGH);   // turn on a 100K pullup internally
    }
    pinMode(ONBOARD_LED, OUTPUT); // use the c13 LED as debugging

    // Sanity check to confirm functionality with onboard LED
    for (int i = 0; i < 8; i++)
    {
        digitalWrite(ONBOARD_LED, HIGH);
        delay(75);
        digitalWrite(ONBOARD_LED, LOW);
        delay(75);
    }
}

void loop()
{

    // Check for a button press of any button.  A LOW indicates a button press
    // There is surely a smarter way to do this, but that never stopped me.
    if (mcp1.digitalRead(0) == 0 ||
        mcp1.digitalRead(1) == 0 ||
        mcp1.digitalRead(2) == 0 ||
        mcp1.digitalRead(3) == 0 ||
        mcp1.digitalRead(4) == 0 ||
        mcp1.digitalRead(5) == 0 ||
        mcp1.digitalRead(6) == 0 ||
        mcp1.digitalRead(7) == 0 ||
        mcp1.digitalRead(8) == 0 ||
        mcp1.digitalRead(9) == 0 ||
        mcp1.digitalRead(10) == 0 ||
        mcp1.digitalRead(11) == 0 ||
        mcp1.digitalRead(12) == 0 ||
        mcp1.digitalRead(13) == 0 ||
        mcp1.digitalRead(14) == 0 ||
        mcp1.digitalRead(15) == 0 ||
        mcp2.digitalRead(0) == 0 ||
        mcp2.digitalRead(1) == 0 ||
        mcp2.digitalRead(2) == 0 ||
        mcp2.digitalRead(3) == 0 ||
        mcp2.digitalRead(4) == 0 ||
        mcp2.digitalRead(5) == 0 ||
        mcp2.digitalRead(6) == 0 ||
        mcp2.digitalRead(7) == 0 ||
        mcp2.digitalRead(8) == 0 ||
        mcp2.digitalRead(9) == 0 ||
        mcp2.digitalRead(10) == 0 ||
        mcp2.digitalRead(11) == 0 ||
        mcp2.digitalRead(12) == 0 ||
        mcp2.digitalRead(13) == 0 ||
        mcp2.digitalRead(14) == 0 ||
        mcp2.digitalRead(15) == 0

    )
    {
        digitalWrite(ONBOARD_LED, LOW); // Turn on the dumb, active low PC13 onboard LED
        delay(1000);                    // Keep the LED on a second.
    }
    else
    {
        digitalWrite(ONBOARD_LED, HIGH); // Turn off the dumb, active low PC13 onboard LED
    }
}