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
}
}