Need help using Control-Surface library. Combining ButtonMatrix and SPI examples

I have an 88 key matrix (8x11) that I've gotten most keys working, but ran out of pins on my Nano. I used the "NoteButtonMatrix.ino" example to get this up and running flawlessly. Now I'm trying to extend my output pins with a 74HC595 shift register, and I'm not sure how to implement the "SPIShiftRegisterOut" function of the library into my code.

I think I have the shift register set up correctly in the code, but how do I substitute those pins into the original "NoteButtonMatrix" function? (Sorry, I don't know how to program.)

PieterP's Control-Surface library found here: GitHub - tttapa/Control-Surface: Arduino library for creating MIDI controllers and other MIDI devices.

(Note, I'm only trying to set up a 8x8 matrix for now so I only have to deal with 1 shift register)

#include <Control_Surface.h>
#include <Arduino_Helpers.h> // Include the Arduino Helpers library.
#include <AH/Hardware/ExtendedInputOutput/SPIShiftRegisterOut.hpp>

HairlessMIDI_Interface hm;

using namespace ExtIO; // Bring the ExtIO pin functions into your sketch

// Instantiate a shift register with the SPI slave select pin as latch pin, most
// significant bit first, and a total of 8 outputs.
SPIShiftRegisterOut<8> sreg = {SS, MSBFIRST};

const pin_t thirdOctave = sreg.pin(0); // first pin of the shift register
const pin_t fourthOctave = sreg.pin(1);
const pin_t fifthOctave = sreg.pin(2);
const pin_t sixthOctave = sreg.pin(3);
const pin_t seventhOctave = sreg.pin(4);
const pin_t eighthOctave = sreg.pin(5);
const pin_t ninthOctave = sreg.pin(6);
const pin_t tenthOctave = sreg.pin(7);

// The note numbers corresponding to the buttons in the matrix
const AddressMatrix<8, 8> addresses = {{
  { 37, 45, 53, 61, 69, 77, 85, 93 },
  { 38, 46, 54, 62, 70, 78, 86, 94 },
  { 39, 47, 55, 63, 71, 79, 87, 95 },
  { 40, 48, 56, 64, 72, 80, 88, 96 },
  { 41, 49, 57, 65, 73, 81, 89, 97 },
  { 42, 50, 58, 66, 74, 82, 90, 98 },
  { 43, 51, 59, 67, 75, 83, 91, 99 },
  { 44, 52, 60, 68, 76, 84, 92, 100 },
}};

NoteButtonMatrix<8, 8> buttonmatrix = {
  {2, 3, 4, 5, 6, 7, 8, 9}, // row pins - Inputs
  {thirdOctave, fourthOctave, fifthOctave, sixthOctave, seventhOctave, eighthOctave, ninthOctave, tenthOctave},    // column pins - Outputs
  addresses,    // address matrix
  CHANNEL_1,    // channel and cable number
};

void setup() {
  sreg.begin();
  Control_Surface.begin();
}

void loop() {
  Control_Surface.loop();
}

The row pins are actually the output pins, not the inputs. See the documentation here:
NoteButtonMatrix

Also see the schematic used in the example:

The columns are always configured as inputs, with the internal pull-up resistors enabled. The code scans through the rows, and pulls one of the rows low to ground by setting it to output mode. The other rows are in input mode.

In the library this happens on these two lines: [25] [36]

Using a normal 74HC595 shift register, you cannot toggle between output mode and input mode, so it cannot be used with the existing code.

There are two alternative solutions:

  1. If your button matrix has diodes built-in as shown in the schematic above, you can safely drive one of the pins low, and leave all others high, both in output mode.

In the code, you have to initialize all row pins as outputs in the begin method, and initialize them to HIGH (using digitalWrite).
Then in the main update method, replace the first pinMode with a digitalWrite to LOW, and the last pinMode with a digitalWrite to HIGH.

  1. If you use an open drain or open collector variant of the '595, driving them LOW actually disables the output completely, and driving them HIGH connects the output to ground.
    You can use the same adaptations in the code as mentioned above, but you'll probably have to invert the logic of the row pins (i.e. replace LOW with HIGH and vice versa).

Do NOT change the code unless your hardware fits one of the two criteria above, because otherwise, you will create a short and blow up the shift registers (or your Arduino) when pressing multiple buttons at the same time.
The diodes or the open drain outputs can prevent that.

Pieter