#include "allFonts.h"
// Radio Buttons!
const int led1Pin = 2; // LED pin number
const int button1 = A0;
const int led2Pin = 3;
const int button2 = A1;
const int led3Pin = 4;
const int button3 = A2;
const int led4Pin = 5;
const int button4 = A3;
char bstate1 = 0;
char bstate2 = 0;
char bstate3 = 0;
char bstate4 = 0;
unsigned long bcount1 = 0; // button debounce timer. Replicate as necessary.
unsigned long bcount2 = 0;
unsigned long bcount3 = 0;
unsigned long bcount4 = 0;
// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check
// Routines by Paul__B of Arduino Forum
boolean timeout(unsigned long *marker, unsigned long interval) {
if (millis() - *marker >= interval) {
*marker += interval; // move on ready for next interval
return true;
}
else return false;
}
// Deal with a button read; true if button pressed and debounced is a new event
// Uses reading of button input, debounce store, state store and debounce interval.
// Routines by Paul__B of Arduino Forum
boolean butndown(char button, unsigned long *marker, char *butnstate, unsigned long interval) {
switch (*butnstate) { // Odd states if was pressed, >= 2 if debounce in progress
case 0: // Button up so far,
if (button == HIGH) return false; // Nothing happening!
else {
*butnstate = 2; // record that is now pressed
*marker = millis(); // note when was pressed
return false; // and move on
}
case 1: // Button down so far,
if (button == LOW) return false; // Nothing happening!
else {
*butnstate = 3; // record that is now released
*marker = millis(); // note when was released
return false; // and move on
}
case 2: // Button was up, now down.
if (button == HIGH) {
*butnstate = 0; // no, not debounced; revert the state
return false; // False alarm!
}
else {
if (millis() - *marker >= interval) {
*butnstate = 1; // jackpot! update the state
return true; // because we have the desired event!
}
else
return false; // not done yet; just move on
}
case 3: // Button was down, now up.
if (button == LOW) {
*butnstate = 1; // no, not debounced; revert the state
return false; // False alarm!
}
else {
if (millis() - *marker >= interval) {
*butnstate = 0; // Debounced; update the state
return false; // but it is not the event we want
}
else
return false; // not done yet; just move on
}
default: // Error; recover anyway
{
*butnstate = 0;
return false; // Definitely false!
}
}
}
// button 5
int LED5 = 6;
int Tbutton5 = A4;
bool toggle1=false;
// button 6
int LED6 = 7;
int Tbutton6 = A5;
bool toggle2=false;
// MIDI Switch v1.2
// 2020-08-01
// Arduino MIDI switch by jimkim.de
// https://www.jimkim.de
// This MIDI device can receive MIDI program change and control change messages
// For each program (preset) the max. 8 outputs can be individually set (settings are saved to non volatile memory)
// Additionally, a MIDI controller can be assigned to each output (e.g. controller number 4 --> output 1)
// Controller values from 0-63 will switch off the output, values from 64-127 will switch it on
// Program change function and control change function can be globally enabled / disabled in the setup menu
// libraries
#include <SoftwareSerial.h>
// use SoftwareSerial lib instead of the serial lib; this lets us control which pins the MIDI interface is connected to
// only RX is needed; set RX = TX pin to save one I/O pin
SoftwareSerial midiSerial(12, 12);
#include <EEPROM.h>
// libs for ssd1306 OLED
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
// OLED address: 0X3C+SA0 - 0x3C or 0x3D
#define I2C_ADDRESS 0x3C
// define proper RST_PIN if required
#define RST_PIN -1
SSD1306AsciiWire oled;
const long displayResetTimer = 1000; // reset time (milliseconds)
unsigned long displayResetTimestamp = 0; // timestamp when display was updated
byte displayReset = 0;
// lib for one button control
#include "OneButton.h"
OneButton button1(A1, true); // setup a new OneButton on pin A1
OneButton button2(A2, true); // setup a new OneButton on pin A2
// define output pins
int outputPins[] = {
2, 3, 4, 5, 6, 7, 8, 9
};
// set this to the required number of outputs (1-8) if you do not use all 8 outputs
// higher output numbers will than be ignored in setup menu etc.
const int outputCount = 8; // the number of output pins
// var containing output setting for each output pin
byte outputPinsState;
// constants
#define MIDI_LED 10
#define EDIT_LED 11
const long midiLedTimer = 250; // MIDI led ON time (milliseconds)
unsigned long midiLedTimestamp = 0; // timestamp when led was activated
#define MIDI_PGM_CHANGE 192
#define MIDI_CTL_CHANGE 176
byte programChangeActive = 1; // switch listens to program change messages
byte controlChangeActive = 1; // switch listens to control change messages
byte statusByte;
byte dataByte1;
byte dataByte2;
byte midiChannel; // MIDI channel data from MIDI in
byte selectedMidiChannel; // currently selected MIDI channel (which channel to listen to)
char midiChannelChar[4];
byte pgmNumber = 0; // init pgmNumber for arduino startup
char pgmNumberChar[4];
byte midiCommand;
byte ctrlNumber = 0; // MIDI controller
char ctrlNumberChar[4];
byte ctrlValue = 0;
char ctrlValueChar[4];
// MIDI controller number assignment (controller number <--> output pin)
int outputPinsControllerNumber[] = {
0, 0, 0, 0, 0, 0, 0, 0
};
char title[16] = "MIDI Switch v1.2";
byte currentSwitchIndex = 0;
byte currentSwitchState = 0;
byte switchChange = 0;
byte editSettings = 0;
byte editSwitches = 0;
byte editProgramChangeFunction = 0;
byte editControlChangeFunction = 0;
byte editControllers = 0;
byte currentControllerIndex = 0;
byte controllerNumberIncrement = 0;
byte valueIncrementSize = 0;
void setup()
{
pinMode(led1Pin, OUTPUT);
pinMode(button1, INPUT);
pinMode(led2Pin, OUTPUT);
pinMode(button2, INPUT);
pinMode(led3Pin, OUTPUT);
pinMode(button3, INPUT);
pinMode(led4Pin, OUTPUT);
pinMode(button4, INPUT);
digitalWrite (led1Pin, LOW);
digitalWrite (led2Pin, LOW);
digitalWrite (led3Pin, LOW);
digitalWrite (led4Pin, LOW);
pinMode(Tbutton5, INPUT);
pinMode(LED5, OUTPUT);
pinMode(Tbutton6, INPUT);
pinMode(LED6, OUTPUT);
Serial.begin(9600); // setup HW serial for debug communication
pinMode(MIDI_LED, OUTPUT);
pinMode(EDIT_LED, OUTPUT);
outputPinsState = EEPROM.read(pgmNumber); // read output switch setting from EEPROM
for (int thisOutput = 0; thisOutput < outputCount; thisOutput++) { // loop over the pin array and set them all to output:
pinMode(outputPins[thisOutput], OUTPUT);
if ( bitRead(outputPinsState, thisOutput) == 1 ) { // if outputState for this pin is active, set HIGH
digitalWrite(outputPins[thisOutput], HIGH);
}
}
selectedMidiChannel = EEPROM.read(500); // read MIDI channel setting from EEPROM
programChangeActive = EEPROM.read(501); // read program change function setting from EEPROM
controlChangeActive = EEPROM.read(502); // read control change function setting from EEPROM
// at very first startup after sketch upload (before any custom MIDI channel has been actively set by user)
// the default EEPROM value may be > 15 if so, set value = 0 (which is MIDI channel 1)
if ( selectedMidiChannel > 15 ) {
selectedMidiChannel = 0;
}
// read MIDI controller number assignment from EEPROM
// special routine because controller number array is int (2 bytes) and needs two EEPROM addresses for each value
int startAddress = 400;
for (int thisOutput = 0; thisOutput < outputCount; thisOutput++) {
outputPinsControllerNumber[thisOutput] = eepromReadInt(startAddress);
startAddress += 2;
}
// setup SoftSerial for MIDI control
midiSerial.begin(31250);
//MIDI.begin(selectedMidiChannel); // launch MIDI listening to selected midi channel
Wire.begin(); // init OLED display
Wire.setClock(400000L);
#if RST_PIN >= 0
oled.begin(&Adafruit128x64, I2C_ADDRESS, RST_PIN);
#else // RST_PIN >= 0
oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif // RST_PIN >= 0
oled.setFont(ZevvPeep8x16);
refreshDisplay(11);
button1.attachClick(click1); // link the button 1 functions
button1.attachLongPressStart(longPressStart1);
button2.attachClick(click2); // link the button 2 functions
button2.attachLongPressStart(longPressStart2);
Serial.println("MIDI Switch is online.");
}
void loop(){
Serial.begin(9600);
// Select LED if button debounced
if (butndown(digitalRead(button4), &bcount4, &bstate4, 2UL )) {
digitalWrite (led1Pin, LOW);
digitalWrite (led2Pin, LOW);
digitalWrite (led3Pin, LOW);
digitalWrite (led4Pin, HIGH);
}
// Select LED if button debounced
if (butndown(digitalRead(button3), &bcount3, &bstate3, 2UL )) {
digitalWrite (led1Pin, LOW);
digitalWrite (led2Pin, LOW);
digitalWrite (led3Pin, HIGH);
digitalWrite (led4Pin, LOW);
}
// Select LED if button debounced
if (butndown(digitalRead(button2), &bcount2, &bstate2, 2UL )) {
digitalWrite (led1Pin, LOW);
digitalWrite (led2Pin, HIGH);
digitalWrite (led3Pin, LOW);
digitalWrite (led4Pin, LOW);
}
// Select LED if button debounced
if (butndown(digitalRead(button1), &bcount1, &bstate1, 2UL )) {
digitalWrite (led1Pin, HIGH);
digitalWrite (led2Pin, LOW);
digitalWrite (led3Pin, LOW);
digitalWrite (led4Pin, LOW);
}
// Button 5 Toggle
if(digitalRead(Tbutton5)){//Cuando el boon se preciona
if(toggle1)
toggle1=false;//Si estaba encendido se apaga
else
toggle1=true;//Si estaba apagado se enciende
}
while(digitalRead(Tbutton5)){}//Se espera hasta que el boton se deje de presionar
//Se cambia el estado de los leds
digitalWrite(LED5,toggle1);
// Button 6 Toggle
if(digitalRead(Tbutton6)){//Cuando el boon se preciona
if(toggle2)
toggle2=false;//Si estaba encendido se apaga
else
toggle2=true;//Si estaba apagado se enciende
}
while(digitalRead(Tbutton6)){}//Se espera hasta que el boton se deje de presionar
//Se cambia el estado de los leds
digitalWrite(LED6,toggle2);
if ( millis() - midiLedTimestamp >= midiLedTimer ) { // turn off MIDI led
digitalWrite(MIDI_LED, LOW); // if timer has run out
}
if ( displayReset == 1 ) { // reset display to default view (MIDI channel)
if ( millis() - displayResetTimestamp >= displayResetTimer ) { // if timer has run out
refreshDisplay(81);
displayReset = 0;
}
}
// is there any MIDI waiting to be read?
if ( midiSerial.available() > 0 && editSettings == 0 ) {
// read MIDI byte
statusByte = midiSerial.read();
// remove program info from status byte (only channel value left)
midiChannel = statusByte & B00001111;
// remove channel info from status byte (only program value left)
midiCommand = statusByte & B11110000;
if ( midiChannel == selectedMidiChannel ) {
delay(2);
switch ( midiCommand ) { // get the type of the message
case MIDI_PGM_CHANGE: // if this is a program change command
pgmNumber = midiSerial.read() + 1; // get program number (data byte 1) from MIDI routine
if ( programChangeActive == 1 ) { // listen to program change?
outputPinsState = EEPROM.read(pgmNumber); // read output switch setting from eeprom for this program number
for ( int thisOutput = 0; thisOutput < outputCount; thisOutput++ ) { // set outputs
if ( bitRead(outputPinsState, thisOutput ) == 1 ) { // if outputState for this pin is active, set HIGH
digitalWrite( outputPins[thisOutput], HIGH );
} else {
digitalWrite( outputPins[thisOutput], LOW );
}
}
refreshDisplay(21);
}
digitalWrite(MIDI_LED, HIGH); // flash MIDI led
midiLedTimestamp = millis();
break;
case MIDI_CTL_CHANGE: // if this is a control change command
ctrlNumber = midiSerial.read(); // get controller number (data byte 1) from MIDI routine
delay(2);
ctrlValue = midiSerial.read(); // get controller value (data byte 2) from MIDI routine
if ( controlChangeActive == 1 ) { // listen to control change?
for (int thisOutput = 0; thisOutput < outputCount; thisOutput++) {
if ( outputPinsControllerNumber[thisOutput] == ctrlNumber ) {
if ( 0 <= ctrlValue && ctrlValue <= 63 ) {
digitalWrite( outputPins[thisOutput], LOW );
}
if ( 64 <= ctrlValue && ctrlValue <= 127 ) {
digitalWrite( outputPins[thisOutput], HIGH );
}
if ( !midiSerial.available() ) {
refreshDisplay(24); // update display
displayReset = 1;
displayResetTimestamp = millis();
}
}
}
}
digitalWrite(MIDI_LED, HIGH); // flash MIDI led
midiLedTimestamp = millis();
break;
// flush other message types
default:
/*
while ( midiSerial.available() ) {
midiSerial.read();
}
*/
break;
}
} /*else {
// flush other MIDI messages
while ( midiSerial.available() ) {
midiSerial.read();
}
}*/ // end check midi channel
} // end receive midi
// edit mode
if ( editSettings == 1 ) {
// edit switches
if ( editSwitches == 1 ) {
// check if output state has been changed
if ( switchChange == 1 ) {
// toggle switch state
if ( bitRead(outputPinsState, currentSwitchIndex) == 1 ) {
digitalWrite(outputPins[currentSwitchIndex], LOW);
bitClear(outputPinsState, currentSwitchIndex);
refreshDisplay(51);
} else {
digitalWrite(outputPins[currentSwitchIndex], HIGH);
bitSet(outputPinsState, currentSwitchIndex);
refreshDisplay(52);
}
switchChange = 0;
}
} // end edit switches
// edit controllers
if ( editControllers == 1 ) {
// check if controller number has been incremented (switch pressed)
if ( controllerNumberIncrement == 1 ) {
if ( valueIncrementSize == 1 ) { // increment controller number by 1
if ( outputPinsControllerNumber[currentControllerIndex] < 127 ) {
outputPinsControllerNumber[currentControllerIndex] += valueIncrementSize;
} else {
outputPinsControllerNumber[currentControllerIndex] = 0;
}
}
if ( valueIncrementSize == 10 ) { // increment controller number by 10
if ( outputPinsControllerNumber[currentControllerIndex] < 118 ) {
outputPinsControllerNumber[currentControllerIndex] += valueIncrementSize;
} else {
outputPinsControllerNumber[currentControllerIndex] = 0;
}
}
controllerNumberIncrement = 0;
refreshDisplay(42);
}
} // end edit controllers
// edit program change function
if ( editProgramChangeFunction == 1 ) {
if ( switchChange == 1 ) { // check if program change function has been changed
if ( programChangeActive == 1 ) { // toggle function state
programChangeActive = 0;
refreshDisplay(43);
} else {
programChangeActive = 1;
refreshDisplay(43);
}
switchChange = 0;
}
} // end edit program change function
// edit control change function
if ( editControlChangeFunction == 1 ) {
if ( switchChange == 1 ) { // check if control change function has been changed
if ( controlChangeActive == 1 ) { // toggle function state
controlChangeActive = 0;
refreshDisplay(44);
} else {
controlChangeActive = 1;
refreshDisplay(44);
}
switchChange = 0;
}
} // end edit control change function
} // end edit
// keep watching the push buttons:
button1.tick();
button2.tick();
}
void click1() {
if ( editSettings == 1 ) {
// edit all switches
if ( editSwitches == 1 ) {
if ( currentSwitchIndex < outputCount - 1 ) {
currentSwitchIndex++;
refreshDisplay(41);
} else {
editSwitches = 0;
currentSwitchIndex = 0;
editControllers = 1;
refreshDisplay(42);
}
return;
}
// edit all controllers
if ( editControllers == 1 ) {
if ( currentControllerIndex < outputCount - 1 ) {
currentControllerIndex++;
refreshDisplay(42);
} else {
editControllers = 0;
currentControllerIndex = 0;
editProgramChangeFunction = 1;
refreshDisplay(43);
}
return;
}
// edit program change function
if ( editProgramChangeFunction == 1 ) {
editProgramChangeFunction = 0;
editControlChangeFunction = 1;
refreshDisplay(44);
return;
}
// edit control change function
if ( editControlChangeFunction == 1 ) {
editControlChangeFunction = 0;
editSwitches = 1;
refreshDisplay(41);
return;
}
}
}
// button 1 long press: start edit mode / exit edit mode
void longPressStart1() {
// toggle edit mode
if ( editSettings == 0) {
// start edit mode
editSettings = 1;
editSwitches = 1;
outputPinsState = EEPROM.read(pgmNumber);
// reset switch index
currentSwitchIndex = 0;
currentControllerIndex = 0;
// refresh display
refreshDisplay(31);
// switch on edit led
digitalWrite(EDIT_LED, HIGH);
} else {
// exit edit mode
editSettings = 0;
editSwitches = 0;
editControllers = 0;
editProgramChangeFunction = 0;
editControlChangeFunction = 0;
// write settings to EEPROM
EEPROM.update(pgmNumber, outputPinsState);
// write controller number assignments to EEPROM
// special routine because controller number array is int (2 bytes) and needs two EEPROM addresses for each value
int startAddress = 400;
for (int thisOutput = 0; thisOutput < outputCount; thisOutput++) {
eepromWriteInt(startAddress, outputPinsControllerNumber[thisOutput]);
startAddress += 2;
}
// write program change and control change function settings to EEPROM
EEPROM.update(501, programChangeActive);
EEPROM.update(502, controlChangeActive);
refreshDisplay(11);
// switch off edit led
digitalWrite(EDIT_LED, LOW);
}
}
// button 2 short press: toggle switch output / increment controller numbers by 1
void click2() {
if ( editSettings == 1 && editSwitches == 1 ) {
if ( switchChange == 0 ) {
switchChange = 1;
}
}
if ( editSettings == 1 && editControllers == 1 ) {
controllerNumberIncrement = 1;
valueIncrementSize = 1;
}
if ( editSettings == 1 && editProgramChangeFunction == 1 ) {
if ( switchChange == 0 ) {
switchChange = 1;
}
}
if ( editSettings == 1 && editControlChangeFunction == 1 ) {
if ( switchChange == 0 ) {
switchChange = 1;
}
}
}
// button 2 long press: edit MIDI channel / increment controller numbers by 10
void longPressStart2() {
if ( editSettings == 0 ) {
// increment MIDI channel and save setting in eeprom
selectedMidiChannel++;
if ( selectedMidiChannel > 15 ) {
selectedMidiChannel = 0;
}
EEPROM.update(500, selectedMidiChannel);
refreshDisplay(81);
}
// if in edit mode (edit controllers) increment controller number by 10
if ( editSettings == 1 && editControllers == 1 ) {
controllerNumberIncrement = 1;
valueIncrementSize = 10;
}
}
// display actions
void refreshDisplay(byte mode) {
// startup
if ( mode == 11 ) {
oled.clear();
oled.set1X();
oled.println(title);
sprintf(midiChannelChar, "%02d", selectedMidiChannel + 1);
oled.print("Channel ");
oled.setCursor(64, 2);
oled.print(midiChannelChar);
oled.setCursor(88, 2);
if ( programChangeActive == 1 ) {
oled.print("PC:");
} else {
oled.print("--:");
}
oled.setCursor(112, 2);
if ( controlChangeActive == 1 ) {
oled.print("CC");
} else {
oled.print("--");
}
oled.set2X();
oled.setCursor(0, 4);
oled.print("PGM:");
sprintf(pgmNumberChar, "%03d", pgmNumber);
oled.setCursor(80, 4);
oled.print(pgmNumberChar);
}
// update program number
if ( mode == 21 ) {
sprintf(pgmNumberChar, "%03d", pgmNumber);
oled.set2X();
oled.setCursor(80, 4);
oled.print(pgmNumberChar);
}
// show control change data
if ( mode == 24 ) {
oled.set1X();
sprintf(ctrlNumberChar, "%03d", ctrlNumber);
sprintf(ctrlValueChar, "%03d", ctrlValue);
oled.setCursor(0, 2);
oled.println("Ctrl:Val ");
oled.setCursor(72, 2);
oled.print(ctrlNumberChar);
oled.setCursor(96, 2);
oled.print(":");
oled.setCursor(104, 2);
oled.print(ctrlValueChar);
}
// enter edit mode
if ( mode == 31 ) {
oled.clear();
oled.set1X();
oled.println(title);
oled.println("Edit Mode:");
oled.set2X();
oled.print("SW : ");
oled.setCursor(32, 4);
oled.print(currentSwitchIndex + 1);
// display output status of current switch
oled.setCursor(80, 4);
if ( bitRead(outputPinsState, currentSwitchIndex) == 1 ) {
oled.print("On ");
} else {
oled.print("Off");
}
}
// update switch index and state
if ( mode == 41 ) {
oled.setCursor(0, 4);
oled.set2X();
oled.print("SW : ");
oled.setCursor(32, 4);
oled.print(currentSwitchIndex + 1);
// display output status of current switch
oled.setCursor(80, 4);
if ( bitRead(outputPinsState, currentSwitchIndex) == 1 ) {
oled.print("On ");
} else {
oled.print("Off");
}
}
// update controller index and number
if ( mode == 42 ) {
oled.setCursor(0, 4);
oled.set2X();
oled.print("CC : ");
oled.setCursor(32, 4);
oled.print(currentControllerIndex + 1);
// display controller number of current controller
oled.setCursor(80, 4);
sprintf(ctrlNumberChar, "%03d", outputPinsControllerNumber[currentControllerIndex]);
oled.println(ctrlNumberChar);
}
// update program change function status
if ( mode == 43 ) {
oled.setCursor(0, 4);
oled.set2X();
oled.print("PC: ");
// display program change function status
oled.setCursor(64, 4);
if ( programChangeActive == 1 ) {
oled.print("On ");
} else {
oled.print("Off");
}
}
// update control change function status
if ( mode == 44 ) {
oled.setCursor(0, 4);
oled.set2X();
oled.print("CC: ");
// display control change function status
oled.setCursor(64, 4);
if ( controlChangeActive == 1 ) {
oled.print("On ");
} else {
oled.print("Off");
}
}
// switch state off
if ( mode == 51 ) {
oled.setCursor(80, 4);
oled.print("Off");
}
// switch state on
if ( mode == 52 ) {
oled.setCursor(80, 4);
oled.print("On ");
}
// update MIDI channel
if ( mode == 81 ) {
oled.set1X();
oled.setCursor(0, 2);
oled.print("Channel ");
sprintf(midiChannelChar, "%02d", selectedMidiChannel + 1);
oled.setCursor(64, 2);
oled.print(midiChannelChar);
oled.setCursor(80, 2);
oled.print(" ");
oled.setCursor(88, 2);
if ( programChangeActive == 1 ) {
oled.print("PC:");
} else {
oled.print("--:");
}
oled.setCursor(112, 2);
if ( controlChangeActive == 1 ) {
oled.print("CC");
} else {
oled.print("--");
}
}
return;
}
// write two byte integer to EEPROM
// data is written to two consecutive EEPROM adresses
void eepromWriteInt(int adr, int value) {
byte low, high;
low = value & 0xFF;
high = (value >> 8) & 0xFF;
EEPROM.update(adr, low);
EEPROM.update(adr + 1, high);
return;
} //eepromWriteInt
// read two byte integer from EEPROM
// data is read from two consecutive EEPROM adresses
int eepromReadInt(int adr) {
byte low, high;
low = EEPROM.read(adr);
high = EEPROM.read(adr + 1);
return low + ((high << 8) & 0xFF00);
} //eepromReadInt