Matrix Key Array Question

that's the code I see in post 13

const AddressMatrix<4, 8> addresses {{
{44, 45, 46, 47, 48, 49, 50, 51},
{52, 53, 54, 55, 56, 57, 58, 59},
{60, 61, 62, 63, 64, 65, 66, 67},
{68, 69, 70, 71, 72, 73, 74, 75},
}};

NoteButtonMatrix<4, 8> buttonmatrix {
{A0,A2,A4,11}, // row pins
{2,3,4,5,6,7,8,9}, // column pins
addresses, // address matrix
CHANNEL_1, // channel and cable number
};

clearly it's not a sketch...

what I was asking is a code using the Control_Surface library if you want to that capture the press and the release of a key. so just using

won't cut it and I don't see in the API for NoteButtonMatrix a way to get notified

I was trying to figure that out as well, I have no idea how to measure the time diff from one switch press to the other using the buttonmatrix class that @PieterP mentioned above. Apparently it is possible with the buttonmatrix class but not with the Notebuttonmatrix class that i used in my sketch because that class only sends note ons and offs.

have you investigated the use of the buttonmatrix class then?

I have tried with no success, because I don't know how to use it without an example sketch. The control surface library src/AH/Hardware only has the source files ButtonMatrix.hpp, ButtonMatrix.ipp & ButtonMatrix.cpp with no examples in examples

there is some doc

https://tttapa.github.io/Control-Surface-doc/Doxygen/dd/d29/classAH_1_1ButtonMatrix.html

update() seems of interest as it's calling `onButtonChanged()

The doxygen docs are very interesting but I am still learning how to use these functions correctly. Could you show me how
image

would be used?

I've never used the library but the doc says it's a callback

so I would declare the buttons as ButtonMatrix and just add a function in the code

void onButtonChanged (uint8_t row, uint8_t col, bool state) {
  Serial.print("button at [");
  Serial.print(col); 
  Serial.print(",");
  Serial.print(row); 
  Serial.print("] is ");
  Serial.println(state ? "ON" : "OFF");
}		

if the library does not open the Serial line, add Serial.begin(115200); in the setup and open the serial monitor at 115200 bauds

Ok, I will try that.

This is what I have so far, I am using control surfaces "MIDI_Monitor" class to print everything out on the seral monitor. Still no clue on creating the second buttonmatrix and timing those events to calculate the velocity.

/** 
 * This example shows prints all MIDI messages that arrive over USB to the 
 * Serial monitor so you can use the Arduino as a MIDI monitor. It also allows 
 * you to enter MIDI messages as text in the Serial monitor, and then sends 
 * these messages as MIDI over USB.
 *
 * @boards  AVR USB, Due, Nano 33 IoT, Nano 33 BLE, Pi Pico, Teensy 3.x
 *
 * This example requires an Arduino board with native MIDI over USB support.
 *
 * To send a MIDI message from the Serial monitor, select “Newline” as the line 
 * ending, enter the MIDI message in hexadecimal, and press enter.  
 * For example, if you enter `90 3C 7F`, a MIDI Note On message for middle C 
 * (0x3C) with a velocity of 127 (0x7F) will be sent over USB.  
 * Spaces between bytes are not required (`903C7F` will be interpreted as 
 * `90 3C 7F`), but they are recommended to prevent framing errors. 
 *
 * @see @ref MIDI_Pipes-Routing.ino
 * @see @ref Debug-MIDI-Interface.ino
 * @see @ref midi_md-routing "MIDI Tutorial: Routing MIDI messages"
 * 
 * Written by PieterP, 2021-06-28  
 * https://github.com/tttapa/Control-Surface
 */

#include <Control_Surface.h> // Include the Control Surface library

// Instantiate the two MIDI interfaces
USBMIDI_Interface midiusb;
USBDebugMIDI_Interface mididbg;

// Instantiate a MIDI pipe to connect the two interfaces
BidirectionalMIDI_Pipe mpipe;

///////////////////Note Button Matrix for sending midi notes//////////////////
const AddressMatrix<4, 8> addresses {{
{44, 45, 46, 47, 48, 49, 50, 51},
{52, 53, 54, 55, 56, 57, 58, 59},
{60, 61, 62, 63, 64, 65, 66, 67},
{68, 69, 70, 71, 72, 73, 74, 75},
}};

NoteButtonMatrix<4, 8> buttonmatrix {
{A0,A2,A4,11}, // row pins
{2,3,4,5,6,7,8,9}, // column pins
addresses, // address matrix
CHANNEL_1, // channel and cable number
};


//////////////Buton Matrix to capture button press events to calculate velocity//////////////////////

















///////////////////////////////////////////////////

void setup() {
  Control_Surface.begin();
  // Create a bidirectional route between the interfaces: 
  // all input to one interface is sent to the output of the other
  midiusb | mpipe | mididbg;
  // Initialize all MIDI interfaces
  MIDI_Interface::beginAll();
}

void loop() {
  Control_Surface.loop();
  // Continuously handle MIDI input
  MIDI_Interface::updateAll();
}

image

I don’t see the callback function

Tried that, but I could not get it to compile, now im thinking I need to calculate the velocity before using the NoteButtonMatrix because the NoteButtonMatrix only sends note ons & offs at velocity 127. So I would probably need to calculate the velocity first and then use the ButtonMatrix class to send the Note Ons & Offs along with the calculated velocity.

#include <Control_Surface.h>
 
USBMIDI_Interface midi;

// Instantiate a transposer that can transpose from 3 octaves up to 3 octaves down.
Transposer<-3, +3> transposer(12);
 
// Instantiate a Selector to change the transposition. N-O buttons connected between ground and digital pins 14 & 15.
IncrementDecrementSelector<transposer.getNumberOfBanks()> selector = {
  transposer,
  {14, 15},
  Wrap::Clamp,
};

// The note numbers corresponding to the buttons in the matrix
const AddressMatrix<4, 8> midiNoteOns {{
{44, 45, 46, 47, 48, 49, 50, 51},
{52, 53, 54, 55, 56, 57, 58, 59},
{60, 61, 62, 63, 64, 65, 66, 67},
{68, 69, 70, 71, 72, 73, 74, 75},
}};

Bankable::NoteButtonMatrix<4, 8> ButtonMatrix {
  transposer,  //work fine
  {A0,A2,A4,11}, // row pins
  {2,3,4,5,6,7,8,9}, // column pinss  
  midiNoteOns,    // address matrix
  CHANNEL_1,    // channel and cable number
};


const AddressMatrix<4, 8> velKeys {{
{1, 2, 3, 4, 5, 6, 7, 8},
{9, 10, 11, 12, 13, 14, 15, 16},
{17, 18, 19, 20, 21, 22, 23, 24},
{25, 26, 27, 28, 29, 30 ,31, 32},
}};

AH::ButtonMatrix<class Derived, 4, 8> velMatrix {
  {A1,A3,A5,12}, // row pins
  {2,3,4,5,6,7,8,9}, // column pins
};
void onButtonChanged (uint8_t row, uint8_t col, bool state) {
  Serial.print("button at [");
  Serial.print(col); 
  Serial.print(",");
  Serial.print(row); 
  Serial.print("] is ");
  Serial.println(state ? "ON" : "OFF");
}


// VELOCITY 7-bit filtered analog input for velocity control. 10k pot pin1=ground, pin2=A10, pin3-vcc.
//FilteredAnalog<7> velocityInput = A10;

 
void setup() {
  Serial.begin(115200);
  Control_Surface.begin();
//  buttonmatrix.setVelocity(100);  //Set velocity for all notes.
}
 
void loop() {
//  if (velocityInput.update())
//    buttonmatrix.setVelocity(velocityInput.getValue());
  
  Control_Surface.loop();
}

Nothing prints to serial monitor this way...

as mentioned you would need a ButtonMatrix not a NoteButtonMatrix

I've never used that so don't know how to, but that's the class accepting the call-back

Thats my issue as well, I don't know how to use the buttonmatrix class, all I have are the .ipp and .hpp source files for it.

#include "ButtonMatrix.hpp"
#include <AH/Containers/CRTP.hpp>
#include <AH/Hardware/ExtendedInputOutput/ExtendedInputOutput.hpp>
#include <string.h>

BEGIN_AH_NAMESPACE

template <class Derived, uint8_t NumRows, uint8_t NumCols>
ButtonMatrix<Derived, NumRows, NumCols>::ButtonMatrix(
    const PinList<NumRows> &rowPins, const PinList<NumCols> &colPins)
    : rowPins(rowPins), colPins(colPins) {
    memset(prevStates, 0xFF, sizeof(prevStates));
}

template <class Derived, uint8_t NumRows, uint8_t NumCols>
void ButtonMatrix<Derived, NumRows, NumCols>::update() {
    unsigned long now = millis();
    // only update 25 ms after previous change (crude software debounce).
    // Edit this in Settings/Settings.hpp
    if (now - prevRefresh < debounceTime)
        return;

    for (size_t row = 0; row < NumRows; row++) { // scan through all rows
        pinMode(rowPins[row], OUTPUT);           // make the current row Lo-Z 0V
#if !defined(__AVR__) && defined(ARDUINO)
        delayMicroseconds(SELECT_LINE_DELAY);
#endif
        for (size_t col = 0; col < NumCols; col++) { // scan through all columns
            bool state = digitalRead(colPins[col]);  // read the state
            if (state != getPrevState(col, row)) {
                // if the state changed since last time
                // execute the handler
                CRTP(Derived).onButtonChanged(row, col, state);
                setPrevState(col, row, state); // remember the state
                prevRefresh = now;
            }
        }
        pinMode(rowPins[row], INPUT); // make the current row Hi-Z again
    }
}

template <class Derived, uint8_t NumRows, uint8_t NumCols>
void ButtonMatrix<Derived, NumRows, NumCols>::begin() {
    // make all columns input pins and enable
    // the internal pull-up resistors
    for (const pin_t &colPin : colPins)
        pinMode(colPin, INPUT_PULLUP);
    // make all rows Hi-Z
    for (const pin_t &rowPin : rowPins)
        pinMode(rowPin, INPUT);
}

template <class Derived, uint8_t NumRows, uint8_t NumCols>
inline uint8_t
ButtonMatrix<Derived, NumRows, NumCols>::positionToBits(uint8_t col,
                                                        uint8_t row) {
    // map from a 2D array of bits to a flat array of bits
    return col * NumRows + row;
}

template <class Derived, uint8_t NumRows, uint8_t NumCols>
inline uint8_t
ButtonMatrix<Derived, NumRows, NumCols>::bitsToIndex(uint8_t bits) {
    return bits >> 3; // bits / 8
}

template <class Derived, uint8_t NumRows, uint8_t NumCols>
inline uint8_t
ButtonMatrix<Derived, NumRows, NumCols>::bitsToBitmask(uint8_t bits) {
    return 1 << (bits & 7); // bits % 8
}

template <class Derived, uint8_t NumRows, uint8_t NumCols>
bool ButtonMatrix<Derived, NumRows, NumCols>::getPrevState(uint8_t col,
                                                           uint8_t row) {
    uint8_t bits = positionToBits(col, row);
    return !!(prevStates[bitsToIndex(bits)] & bitsToBitmask(bits));
}

template <class Derived, uint8_t NumRows, uint8_t NumCols>
void ButtonMatrix<Derived, NumRows, NumCols>::setPrevState(uint8_t col,
                                                           uint8_t row,
                                                           bool state) {
    uint8_t bits = positionToBits(col, row);
    if (state)
        prevStates[bitsToIndex(bits)] |= bitsToBitmask(bits);
    else
        prevStates[bitsToIndex(bits)] &= ~bitsToBitmask(bits);
}

END_AH_NAMESPACE

looking at high level in the source code, probably the call back would need to be in a subclass.

May be @PieterP has a good advice for you

I don't have much time right now, but the keyword to search for is CRTP. It is used inside of the Control Surface library to allow code reuse of the internal classes.

That being said, I don't believe that using the ButtonMatrix class is the simplest solution to your problem. Simply writing your own matrix scanning code based on the few lines of code I cited is going to be simpler than reusing the entire class.

1 Like

I am assuming that you are referring to the code that you cited in post #5 ??

Indeed.

Could you please write a quick example showing how some of this logic could be used?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.