Sorry, I just now checked back on this thread. Here's my code:
#include <OneButton.h>
#include <EEPROM.h>
#include <MIDI.h>
#include <midi_Defs.h>
#include <midi_Message.h>
#include <midi_Namespace.h>
#include <midi_Settings.h>
#define RLY1 11 //set pin for Relay1
#define RLY2 10 //set pin for Relay2
#define RLY3 9 //set pin for Relay3
#define RLY4 8 //set pin for Relay4
#define REVC 7 //set pin for Reverb control (jfet)
#define SW1 12 //set pin for button
MIDI_CREATE_DEFAULT_INSTANCE();
byte curr_program; //current PC value
byte state; //current state
byte n = 1; //these are for cycling states
byte neq = 0;
unsigned long startMillis; //for getting relays to activate simultaneously
unsigned long currentMillis;
const unsigned long period = 50; //set delay for RLY4
bool eq;
bool ch1;
bool ch2;
bool ch3;
bool rev;
// Setup a new OneButton on pin 11.
OneButton button1(SW1, true);
// -----------------------------------------------------------------------------
// This function will be automatically called when a NoteOn is received.
// It must be a void-returning function with the correct parameters,
// see documentation here:
// http://arduinomidilib.fortyseveneffects.com/a00022.html
void handleNoteOn(byte channel, byte pitch, byte velocity)
{
// Do whatever you want when a note is pressed.
// Try to keep your callbacks short (no delays ect)
// otherwise it would slow down the loop() and have a bad impact
// on real-time performance.
}
void handleNoteOff(byte channel, byte pitch, byte velocity)
{
// Do something when the note is released.
// Note that NoteOn messages with 0 velocity are interpreted as NoteOffs.
}
void handleProgramChange(byte channel, byte number)
{
//do something when receiving PC messages
byte value;
curr_program = number; //stores the PC number for later
//read preset value from EEPROM
value = EEPROM.read(number);
if (getBit(value, 1))
{
digitalWrite(RLY4, HIGH);
}
if (getBit(value, 2))
{
digitalWrite(RLY1, HIGH);
delay(period);
digitalWrite(RLY1, LOW);
}
if (getBit(value, 3))
{
digitalWrite(RLY2, HIGH);
delay(period);
digitalWrite(RLY2, LOW);
}
if (getBit(value, 4))
{
digitalWrite(RLY3, HIGH);
delay(period);
digitalWrite(RLY3, LOW);
}
if (getBit(value, 5) && getBit(value, 2))
{
digitalWrite(LED_BUILTIN, HIGH);
}
else
{
digitalWrite(LED_BUILTIN, LOW);
}
}
// This function will be called when the button1 was pressed 1 time.
void click1() {
eq = false;
ch1 = false;
ch2 = false;
ch3 = false;
byte temp = 1 << n;
state = temp + neq;
if (getBit(state, 1))
{
digitalWrite(RLY4, HIGH);
eq = true;
neq = 0;
n++;
}
else
{
neq = 1;
}
if (getBit(state, 4))
{
digitalWrite(RLY3, HIGH);
delay(period);
digitalWrite(RLY3, LOW);
ch3 = true;
digitalWrite(LED_BUILTIN, LOW);
}
if (getBit(state, 3))
{
digitalWrite(RLY2, HIGH);
delay(period);
digitalWrite(RLY2, LOW);
ch2 = true;
digitalWrite(LED_BUILTIN, LOW);
}
if (getBit(state, 2))
{
digitalWrite(RLY1, HIGH);
delay(period);
digitalWrite(RLY1, LOW);
ch1 = true;
}
if (state == 9) //restart cycling all states
{
n = 1;
neq = 0;
}
} // click1
// This function will be called when the button1 was pressed 2 times in a short timeframe.
void doubleclick1() { //toggle reverb
if (rev == false && ch1)
{
digitalWrite(LED_BUILTIN, HIGH);
rev = true;
}
else
{
digitalWrite(LED_BUILTIN, LOW);
rev = false;
}
} // doubleclick1
// This function will be called once, when the button1 is pressed for a long time.
void longPressStart1() {
//save preset
byte value;
if (eq)
{
setBit(value, 1);
//Serial.print("eq ");
}
if (ch1)
{
setBit(value, 2);
//Serial.print("ch1 ");
}
if (ch2)
{
setBit(value, 3);
//Serial.print("ch2 ");
}
if (ch3)
{
setBit(value, 4);
//Serial.print("ch3 ");
}
if (digitalRead(LED_BUILTIN) && ch1)
{
setBit(value, 5);
//Serial.print(" rev");
}
EEPROM.write(curr_program, value);
//Serial.println(value, BIN);
} // longPressStart1
// This function will be called often, while the button1 is pressed for a long time.
void longPress1() {
//Serial.println("Button 1 longPress...");
} // longPress1
// This function will be called once, when the button1 is released after beeing pressed for a long time.
void longPressStop1() {
//digitalWrite(RLY2,LOW);
} // longPressStop1
// -----------------------------------------------------------------------------
void setup()
{
pinMode(RLY1, OUTPUT);
pinMode(RLY2, OUTPUT);
pinMode(RLY3, OUTPUT);
pinMode(RLY4, OUTPUT);
pinMode(REVC, OUTPUT);
pinMode(SW1, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
//write inital presets, only for debugging.
for (int i = 0; i < 128; i++)
{
//EEPROM.write(i,i);
}
//Define button handling functions
button1.attachClick(click1);
button1.attachDoubleClick(doubleclick1);
button1.attachLongPressStart(longPressStart1);
button1.attachLongPressStop(longPressStop1);
button1.attachDuringLongPress(longPress1);
// Connect the handleNoteOn function to the library,
// so it is called upon reception of a NoteOn.
MIDI.setHandleNoteOn(handleNoteOn); // Put only the name of the function
// Do the same for NoteOffs
MIDI.setHandleNoteOff(handleNoteOff);
// And for PC message
MIDI.setHandleProgramChange(handleProgramChange);
// Initiate MIDI communications, listen to all channels
MIDI.begin(MIDI_CHANNEL_OMNI);
//Serial.begin(9600); //Set serial to 9600, only for debugging
//Serial.println("Start");
}
void loop()
{
// Call MIDI.read the fastest you can for real-time performance.
MIDI.read();
// There is no need to check if there are messages incoming
// if they are bound to a Callback function.
// The attached method will be called automatically
// when the corresponding message has been received.
// Call button tick to check button status
button1.tick();
// See if RLY4 is on, turn it off after preset period
currentMillis = millis();
if (digitalRead(RLY4));
{
startMillis = millis(); //start counting milliseconds if RLY4 on
if (currentMillis - startMillis >= period)
{
digitalWrite(RLY4, LOW);
}
}
}
byte setBit(byte &store, byte bitn) { //bit 1 is right-most
store |= (1 << (bitn - 1)); //set bit 5 to '1'.
}
byte clearBit(byte &store, byte bitn) {
store &= !(1 << (bitn - 1));
}
byte getBit(byte store, byte bitn) {
byte b = (1 << (bitn - 1));
return (store & b);
}
It's been a while since I wrote this, but I'll try to explain. I'm not really a programmer at all, so the code is probably a bit of a hack job and it's still a work in progress, but it works for now.
The sound preset (channel, eq and reverb on or off) is stored as byte 'value' which has 5 bits. The least significant bit indicates EQ state, the next three are the preamp channels 1 to 3, bit 5 is the CH1 reverb (although I haven't implemented this in my preamp yet, so it just lights the inbuilt led).
So CH1 with no EQ is 00010, CH1 with EQ is 00011, CH2 without EQ is 00100, CH2 with EQ is 00101, CH3 without EQ is 01000, CH3 with EQ is 01001. The reverb for CH1 is activated by a double click, and the current program is saved by a long press.
Also note that all of these different states are non-latching, so they only give a pulse of 50 ms (controlled by "const unsigned long period"), you might need to change it to operate as latching.
Hope this helps!