Control-Surface
Control-Surface copied to clipboard
Provide a generic example for ButtonMatrix callback in the main sketch
Being very new to C++ development I was looking for a clean way to execute arbitrary code in the main sketch - triggered by the NoteButtonMatrix. My first use case was controlling WS2812B LEDs, which are driven by another library.
I haven't found an example in this repo/issues to do this. Existing LED examples are great but in all cases are driven by the Incoming MIDI messages. In my case WS2812B LEDs need to be driven individually with the push of a button in NoteButtonMatrix, independent of MIDI messages.
Initially I thought including LED library into Control Surface was a good idea, but then I found a much cleaner way - to register a function from the main sketch as a callback in Control Surface.
Sketch:
// Main sketch
#include <Control_Surface.h>
#include <WS2812Serial.h>
// MIDI
USBMIDI_Interface usbmidi;
HardwareSerialMIDI_Interface serialmidi = {Serial1, MIDI_BAUD};
BidirectionalMIDI_PipeFactory<2> pipes;
// MATRIX
const AddressMatrix<4, 4> addresses = {{
{60,61,62,63},
{64,65,66,67},
{68,69,70,71},
{72,73,74,75}
}};
NoteButtonMatrix<4, 4> buttonmatrix = {
{19,18,17,16}, // row pins
{6,7,8,9}, // column pins
addresses, // address matrix
CHANNEL_1, // channel and cable number
};
// LED
const int numled = 16;
const int pin = 14;
byte drawingMemory[numled*3]; // 3 bytes per LED
DMAMEM byte displayMemory[numled*12]; // 12 bytes per LED
WS2812Serial leds(numled, displayMemory, drawingMemory, pin, WS2812_GRB);
#define RED 0xFF0000
#define GREEN 0x00FF00
int ledArray[4][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11},
{12,13,14,15}
};
void setActiveButtonLed(int row, int col, uint32_t color) {
leds.setPixel(ledArray[row][col], color);
leds.show();
}
void setup() {
// MIDI
Control_Surface | pipes | usbmidi;
Control_Surface | pipes | serialmidi;
Control_Surface.begin();
Serial.begin(115200);
// LED
leds.setBrightness(20);
leds.begin();
for (int led = 0; led < numled; led++){ // Startup animation
leds.setPixel(led, GREEN);
delay(100);
leds.show();
}
Control_Surface.set_color_callback(setActiveButtonLed);
}
void loop() {
Control_Surface.loop();
}
Of interest here is Control_Surface.set_color_callback(setActiveButtonLed);
Here's the way I went about it in the library code:
- In
Control Surface/src/Control_Surface/Control_Surface_Class.hpp
I added the following function pointer and a public function
using m_cb = void (*)(int, int, uint32_t); // function pointer
public:
m_cb set_active_color_callback;
void set_color_callback(m_cb act)
{
set_active_color_callback = act;
}
- In
Control Surface/src/MIDI_Outputs/Abstract/MIDIButtonMatrix.hpp
I extended the privateonButtonChanged
function as such to callControl_Surface.set_active_color_callback
. I feel there should be a cleaner way to executeset_active_color_callback
?
private:
void onButtonChanged(uint8_t row, uint8_t col, bool state) final override {
#define RED 0xFF0000
#define GREEN 0x00FF00
int8_t address = addresses[row][col];
if (state == LOW) {
sender.sendOn({address, baseChannelCN});
Control_Surface.set_active_color_callback(row, col, RED); //invoke the callback
}
else {
sender.sendOff({address, baseChannelCN});
Control_Surface.set_active_color_callback(row, col, GREEN); //invoke the callback
}
}
This works as expected - a push of the button in the matrix triggers setActiveButtonLed()
and passes row, col, color to it, which in turn selects the correct LED from ledArray
and turns it RED/GREEN.
The reason I added the callback pointer/function to the Control_Surface
class is to be able to access it from the main sketch. But I am not sure if calling Control_Surface.set_active_color_callback()
from the library is the best way to do it?
Would like some feedback/improvement advice and maybe we could add something like this to examples?
Additionally I am curious about portability of such modification through library updates. Will a future update wipe my changes? What's the best way to persist them (other than maintaining a fork of this library)?
Many thanks for this great library and the top level of support you provide here!
It's not clear to me what the advantage is of adding the callback to the Control_Surface_
class. At first sight, the colors and events are all tied to the button matrix, and have nothing to do with Control_Surface_
itself. This approach doesn't generalize.
Why not add the callback to the button matrix?
If you want to be able to easily update the library later, you shouldn't change any of the source files. Everything you describe can be done without altering the library, by defining your own MIDI elements, as shown in this example: https://tttapa.github.io/Control-Surface-doc/Doxygen/d4/d3b/Custom-MIDI-Output-Element_8ino-example.html
You can use the MIDIButtonMatrix
as a starting point and add your specific behavior and LED logic to your own custom version of that class.