#include <Arduino.h>
#include <map>
// for displaying the CC label
std::map<int, String> cc_map;
// Configuration settings
const int NUM_SWITCHES = 6; // Number of switches
enum SwitchMode {
MOMENTARY, // 0
LATCH // 1
};
enum ActionType {
NOTE, // 0
CC // 1, continuous control
};
struct SwitchConfig {
int channel = 1;
int switch_pin;
int led_pin;
SwitchMode mode = MOMENTARY;
ActionType action = NOTE;
// cc or note on state event
int on_num = 0; // the note or CC number
int on_value = 127; // the note velocity or CC value
// cc off state event
int off_num = 0;
int off_value = 127;
};
SwitchConfig switch_config[NUM_SWITCHES];
// State variables
bool lastSwitchStates[NUM_SWITCHES] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH}; // Initialize with HIGH assuming INPUT_PULLUP
bool currentSwitchStates[NUM_SWITCHES] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH}; // Current stable states
unsigned long lastDebounceTimes[NUM_SWITCHES] = {0, 0, 0, 0, 0, 0}; // Last times the switch states changed
const unsigned long debounceDelay = 50; // Debounce time in milliseconds
bool latchState[NUM_SWITCHES] = {LOW, LOW, LOW, LOW, LOW, LOW}; // Initial latch states
void init_cc_map() {
cc_map[-1] = "Not set";
cc_map[0] = "Bank Select (MSB)";
cc_map[1] = "Modulation Wheel";
cc_map[2] = "Breath controller";
cc_map[3] = "Undefined";
cc_map[4] = "Foot Pedal (MSB)";
cc_map[5] = "Portamento Time (MSB)";
cc_map[6] = "Data Entry (MSB)";
cc_map[7] = "Volume (MSB)";
cc_map[8] = "Balance (MSB";
cc_map[9] = "Undefined";
cc_map[10] = "Pan position (MSB)";
cc_map[11] = "Expression (MSB)";
cc_map[12] = "Effect Control 1 (MSB)";
cc_map[13] = "Effect Control 2 (MSB)";
cc_map[14] = "Undefined";
cc_map[15] = "Undefined";
cc_map[16] = "General Purpose";
cc_map[17] = "General Purpose";
cc_map[18] = "General Purpose";
cc_map[19] = "General Purpose";
cc_map[20] = "Undefined";
cc_map[21] = "Undefined";
cc_map[22] = "Undefined";
cc_map[23] = "Undefined";
cc_map[24] = "Undefined";
cc_map[25] = "Undefined";
cc_map[26] = "Undefined";
cc_map[27] = "Undefined";
cc_map[28] = "Undefined";
cc_map[29] = "Undefined";
cc_map[30] = "Undefined";
cc_map[31] = "Undefined";
cc_map[32] = "Controller 0";
cc_map[33] = "Controller 1";
cc_map[34] = "Controller 2";
cc_map[35] = "Controller 3";
cc_map[36] = "Controller 4";
cc_map[37] = "Controller 5";
cc_map[38] = "Controller 6";
cc_map[39] = "Controller 7";
cc_map[40] = "Controller 8";
cc_map[41] = "Controller 9";
cc_map[42] = "Controller 10";
cc_map[43] = "Controller 11";
cc_map[44] = "Controller 12";
cc_map[45] = "Controller 13";
cc_map[46] = "Controller 14";
cc_map[47] = "Controller 15";
cc_map[48] = "Controller 16";
cc_map[49] = "Controller 17";
cc_map[50] = "Controller 18";
cc_map[51] = "Controller 19";
cc_map[52] = "Controller 20";
cc_map[53] = "Controller 21";
cc_map[54] = "Controller 22";
cc_map[55] = "Controller 23";
cc_map[56] = "Controller 24";
cc_map[57] = "Controller 25";
cc_map[58] = "Controller 26";
cc_map[59] = "Controller 27";
cc_map[60] = "Controller 28";
cc_map[61] = "Controller 29";
cc_map[62] = "Controller 30";
cc_map[63] = "Controller 31";
cc_map[64] = "Hold Pedal (on/off)";
cc_map[65] = "Portamento (on/off)";
cc_map[66] = "Sostenuto Pedal (on/off)";
cc_map[67] = "Soft Pedal (on/off)";
cc_map[68] = "Legato Pedal (on/off)";
cc_map[69] = "Hold 2 Pedal (on/off)";
cc_map[70] = "Sound Variation";
cc_map[71] = "Resonance (Timbre)";
cc_map[72] = "Sound Release Time";
cc_map[73] = "Sound Attack Time";
cc_map[74] = "Frequency Cutoff (Brightness)";
cc_map[75] = "Sound Control 6";
cc_map[76] = "Sound Control 7";
cc_map[77] = "Sound Control 8";
cc_map[78] = "Sound Control 9";
cc_map[79] = "Sound Control 10";
cc_map[80] = "Decay or General Purpose Button 1 (on/off) Roland Tone level 1";
cc_map[81] = "Hi Pass Filter Frequency or General Purpose Button 2 (on/off) Roland Tone level 2";
cc_map[82] = "General Purpose Button 3 (on/off) Roland Tone level 3";
cc_map[83] = "General Purpose Button 4 (on/off) Roland Tone level 4";
cc_map[84] = "Portamento Amount";
cc_map[86] = "Undefined";
cc_map[87] = "Undefined";
cc_map[88] = "Undefined";
cc_map[89] = "Undefined";
cc_map[90] = "Undefined";
cc_map[91] = "Reverb Level";
cc_map[92] = "Tremolo Level";
cc_map[93] = "Chorus Level";
cc_map[94] = "Detune Level";
cc_map[95] = "Phaser Level";
cc_map[96] = "Data Button increment";
cc_map[97] = "Data Button decrement";
cc_map[98] = "Non-registered Parameter (LSB)";
cc_map[99] = "Non-registered Parameter (MSB)";
cc_map[100] = "Registered Parameter (LSB)";
cc_map[101] = "Registered Parameter (MSB)";
cc_map[102] = "Undefined";
cc_map[103] = "Undefined";
cc_map[104] = "Undefined";
cc_map[105] = "Undefined";
cc_map[106] = "Undefined";
cc_map[107] = "Undefined";
cc_map[108] = "Undefined";
cc_map[109] = "Undefined";
cc_map[110] = "Undefined";
cc_map[111] = "Undefined";
cc_map[112] = "Undefined";
cc_map[113] = "Undefined";
cc_map[114] = "Undefined";
cc_map[115] = "Undefined";
cc_map[116] = "Undefined";
cc_map[117] = "Undefined";
cc_map[118] = "Undefined";
cc_map[119] = "Undefined";
cc_map[120] = "All Sound Off";
cc_map[121] = "All Controllers Off";
cc_map[122] = "Local Keyboard (on/off)";
cc_map[123] = "All Notes Off";
cc_map[124] = "Omni Mode Off";
cc_map[125] = "Omni Mode On";
cc_map[126] = "Mono Operation";
cc_map[127] = "Poly Mode";
}
// Initialize switch configurations
void init_switches() {
const int START_SW_PIN = 2;
const int START_LED_PIN = 8;
const int START_MIDI_NOTE = 36; // C2
for (int i = 0; i < NUM_SWITCHES; i++) {
SwitchConfig& sw = switch_config[i];
sw.switch_pin = START_SW_PIN + i;
sw.led_pin = START_LED_PIN + i;
sw.on_num = START_MIDI_NOTE + i;
sw.on_value = 127;
// accept defaults for other values
pinMode(sw.switch_pin, INPUT_PULLUP);
pinMode(sw.led_pin, OUTPUT);
}
// variations to test
// switch_config[0].mode = MOMENTARY;
switch_config[0].action = CC;
switch_config[0].on_num = 65; // portamento on/off
switch_config[0].on_value = 127;
switch_config[0].off_num = 65; // portamento on/off
switch_config[0].off_value = 0;
switch_config[5].mode = LATCH;
}
void switch_config_dump() {
for (int i = 0; i < NUM_SWITCHES; i++) {
SwitchConfig& sw = switch_config[i];
Serial.print("<S");
Serial.print(i+1);
Serial.print(" Pin=");
Serial.print(sw.switch_pin);
Serial.print(" LED=");
Serial.print(sw.led_pin);
Serial.print(" Mode=");
Serial.print(sw.mode == MOMENTARY ? "MOMENTARY" : "LATCH");
Serial.print(", Action=");
Serial.print(sw.action == NOTE ? "NOTE" : "CC");
Serial.print(", MIDI Number=");
Serial.print(sw.on_num);
Serial.print(", MIDI Value=");
Serial.print(sw.on_value);
Serial.println(">");
}
}
void px_midi_note(SwitchConfig& sw, bool state) {
Serial.print("NOTE: ");
Serial.print(sw.on_num);
Serial.print("@");
Serial.print(sw.on_value);
Serial.print(" ");
Serial.println(state ? "ON" : "OFF");
}
void px_midi_cc(SwitchConfig& sw, bool state) {
Serial.print("CC: ");
if (state && sw.on_value != -1) {
Serial.print(cc_map[sw.on_num]);
Serial.print(" (");
Serial.print(sw.on_num);
Serial.print(") @ ");
Serial.println(sw.on_value);
}
if (!state && sw.off_value != -1) {
Serial.print(cc_map[sw.off_num]);
Serial.print(" (");
Serial.print(sw.off_num);
Serial.print(") @ ");
Serial.println(sw.off_value);
}
}
void setup() {
Serial.begin(115200);
init_cc_map();
init_switches();
switch_config_dump();
}
void loop() {
for (int i = 0; i < NUM_SWITCHES; i++) {
SwitchConfig& sw = switch_config[i];
int reading = digitalRead(sw.switch_pin);
// Check if the switch state has changed
if (reading != lastSwitchStates[i]) {
// Reset debounce timer
lastDebounceTimes[i] = millis();
}
// Only update the current state if the switch state has stabilized
if ((millis() - lastDebounceTimes[i]) > debounceDelay) {
// Update the stable state if it has changed
if (reading != currentSwitchStates[i]) {
currentSwitchStates[i] = reading;
// Handle LED based on the mode of each switch
bool state = false;
if (sw.mode == LATCH) { // Latch mode
if (reading == LOW) {
state = !latchState[i];
latchState[i] = state;
} else {
state = latchState[i];
}
} else { // Momentary mode
state = !reading;
}
if (sw.action == NOTE) {
// we want to send the start and end
if (sw.mode == MOMENTARY || reading == LOW) {
px_midi_note(sw, state);
}
} else {
// we only send the ON in latch mode
// if we don't check reading as well, we get two hits (transition to state,
// and first off)
if (sw.mode == MOMENTARY || (sw.mode == LATCH && reading == LOW)) {
px_midi_cc(sw, state);
}
}
// Turn LED on or off
digitalWrite(sw.led_pin, state);
}
}
// Save the current switch state for the next loop iteration
lastSwitchStates[i] = reading;
}
}