I am starting to build a Arduino (or Teensy) based MIDI Controller. Before getting into the MIDI-area my first goal is to read out 16 Buttons and print a status change on the serial monitor. (To keep it simple, I started with only 8)
Thanks to @MicroBahner the first sketch without multiplexing works pretty fine, exactly the way I need it:
#include <Bounce2.h>
const byte buttonPins[] = { 4, 5, 6, 7, 8, 9, 10, 11 };
const byte buttonCnt = sizeof(buttonPins);
Bounce button[buttonCnt] ;
void setup() {
Serial.begin(9600);
for ( byte i = 0; i < buttonCnt; i++ ) {
button[i].attach( buttonPins[i], INPUT_PULLUP );
button[i].interval(5); // debounce time
}
}
void loop() {
for ( byte i = 0; i < buttonCnt; i++ ) button[i].update();
for ( byte i = 0; i < buttonCnt; i++ ) {
if (button[i].fallingEdge()) {
Serial.print("Button "); Serial.print(i); Serial.println(" was pressed");
}
if (button[i].risingEdge()) {
Serial.print("Button "); Serial.print(i); Serial.println(" was released");
}
}
}
But now, as I am trying to include a 74HC4051 multiplexer to reduce the involved pins (wiring see below), I need some help to translate the following code (taken from one of the examples from the "control surface" library:
/**
* Written by Pieter P, 2019-08-08
* https://github.com/tttapa/Arduino-Helpers
*/
#include <Arduino_Helpers.h> // Include the Arduino Helpers library
#include <AH/Hardware/ExtendedInputOutput/AnalogMultiplex.hpp>
CD74HC4051 mux = {
3,
{0, 1, 2},
};
void setup() {
Serial.begin(115200);
mux.begin(); // Initialize multiplexer
mux.pinMode(0, INPUT_PULLUP); // Set the pin mode (setting it for one pin of
// the multiplexers sets it for all of them)
}
void loop() {
for (pin_t pin = 0; pin < mux.getLength(); ++pin) {
Serial.print(mux.digitalRead(pin));
Serial.print('\t');
}
Serial.println();
}
In this sketch, the serial monitor constantly prints all the button states. Can anyone show me how to change this, so it only prints a corresponding message, if the status of a button is changed? (Like in the first code example)
See the StateChangeDetection example in the IDE and put the previous states in an array for comparison with the current state then print a message if one of them changes
With multiplexing many buttons the MoToButtons class of the MobaTools library may be worth a try. This class can manage up to 32 buttons in one instance. The 'raw state' of the buttons can be read by means of a callback function. This allows for arbitrary key arrangements. Even a matrix via I2C would be possible. Of course reading via HC4051 is also no problem.
Because the complete MobaTools library doesn't run on teensy yet, you have to copy the MoToButtons.h file into your sketch directory and include it from there. MoToButtons does not need any HW specific items and will therefor run on teensy too. MotoButtons.h contains the complete class definition, so you need nothing else.
unsigned statusNow = 0;
static unsigned statusLastTime; // a place to save bits
for (pin_t pin = 0; pin < mux.getLength(); ++pin) {
Serial.print(mux.digitalRead(pin));
bitWrite(statusNow, pin, digitalRead(pin)); // <- added
Serial.print('\t');
}
// after collecting all the bits, compare them to the previous state
if (statusLastTime != statusNow) {
// something changed, do stuff
statusLastTime = statusNow; // reset the status memory
}
digitalWrite (loadPin, LOW);
digitalWrite (loadPin, HIGH); // latch the data at the inputs
SPI.transfer(byte1);
SPI.transfer (byte2);
Have pullups on each input so they read high when button is not pressed.
if ((byte1==0xff) && (byte2 ==0xff))
{
// no button was pressed
buttonPressed = 0;
}
else {
buttonPressed = 1;
}
if (buttonPressed == 1){
// do something with the data
...
}
Control Surface comes with a Button class that does roughly the same as the Bounce library, and it supports multiplexers out of the box:
#include <Control_Surface.h>
CD74HC4051 mux {
3,
{0, 1, 2},
};
// Convert the array of pins of the multiplexer to an
// array of debounced button objects
Array<Button, 8> buttons = copyAs<Button>(mux.pins());
void setup() {
Serial.begin(115200);
mux.begin();
for (Button &button : buttons)
button.begin();
}
void loop() {
uint8_t i = 0;
for (Button &button : buttons) {
switch (button.update()) {
case Button::Pressed: break;
case Button::Released: break;
case Button::Falling: Serial << "Button " << i << " was pressed" << endl; break;
case Button::Rising: Serial << "Button " << i << " was released" << endl; break;
}
++i;
}
}
However, none of this is necessary if you just want some buttons that send MIDI messages when pressed/released:
Hi, @PieterP !
Yep, this is what I asked for, so I'll mark this thread as solved. However, actually my goal is HUI protocol. So instead of sending one MIDI note per pressing / releasing a button, I'd like to send two cc messages for each single action. As I am still a beginner, I need some time to figure that out..
Right before I saw your kind response I was skipping through the control surface examples.. quite extensive
Yes indeed - which was the exact moment when I ordered my ArduinoUno starter kit and there we are. Still impressed and optimistic. Will have a close look at your examples
after browsing through the various examples from @PieterP 's Control Surface library I now stuck with the one called "Toggle-LEDs", because shift registers for LEDs were prepared (next steps..) But somehow I can't get it to send cc messages for pressing and releasing the buttons. I assume there is something wrong in the if statement? (see below) Help is very much appreciated.
Both statements on their own ("Falling", "Rising") seem to give the right results, but I need both...
Sincerely, Philip
/**
Most of this sketch taken from Example "Toggle-LEDs" by PieterP, 2018-08-28
*/
#include <Arduino_Helpers.h> // Include the Arduino Helpers library.
#include <AH/Containers/ArrayHelpers.hpp>
#include <AH/Hardware/Button.hpp>
#include <AH/Hardware/ExtendedInputOutput/AnalogMultiplex.hpp>
#include <AH/Hardware/ExtendedInputOutput/SPIShiftRegisterOut.hpp>
const int channel = 1;
const int port = 2;
CD74HC4051 mux = {
3,
{0, 1, 2},
};
SPIShiftRegisterOut<mux.length()> sreg = {SS, MSBFIRST};
auto buttons = generateIncrementalArray<Button, mux.length()>(mux.pin(0));
void setup() {
mux.begin();
sreg.begin();
for (Button &button : buttons)
button.begin();
}
void loop() {
for (uint8_t i = 0; i < mux.length(); ++i) {
if (buttons[i].update() == Button::Falling) {
usbMIDI.sendControlChange(0x0F, i, channel);
usbMIDI.sendControlChange(0x2F, (0x40 + port), channel);
}
if (buttons[i].update() == Button::Rising) {
usbMIDI.sendControlChange(0x0F, i, channel);
usbMIDI.sendControlChange(0x2F, port, channel);
}
}
}
You're calling buttons[i].update() twice, so you're losing edges. Call Button::update() once, then use Button::getState() to get the state without updating it first.
Thanks @PieterP again, the 16 buttons are successfully sending now. I'll move over to the LEDs, maybe with new help requests. On a different thread though.