Here is the code without the OLED part.
I takes 1.7 kb out of the 2.5kb available on the Arduino Micro
#include <MIDI.h>
#include <TimedBlink.h>
#include <Keyboard.h>
#include <Bounce2.h>
#include <Bounce2.h>
#include <EEPROM.h>
#include "Wire.h"
#include <U8glib.h>
#define DEBUG_MODE true
#define DEBUG_MSG \
if (DEBUG_MODE) Serial
const byte NUM_PAGES = 4;
const byte NUM_BUTTONS = 10;
const byte NUMBER_LED_RING = 6;
const int TIME_LONGPRESS = 500;
const byte RELAY_PIN = 8;
const byte NUM_PIN_CONTROLLED_BY_REGISTERS = 8;
const uint8_t BUTTON_ARDUINO_PINS[NUM_BUTTONS] = {A4, A3, 4, 5, 6, A2, A1, 9, 8, 7};
//EXP ON A5 - blue cable
const uint8_t LED_PINS[NUMBER_LED_RING * 2] = {6,5,7,8,4,8,3,8,2,1,0,8};
//using 8 for LED not hooked, the structure follows button1 color 1,button 1 color 2 etc...
//
/* Line 6 HX Stomp constants
* -------------------------
*/
const byte STOMP_MODE = 0;
const byte SCROLL_MODE = 1;
const byte PRESET_MODE = 2;
const byte SNAP_MODE = 3;
const byte SNAPSHOT_CC = 69;
const byte SNAPSHOT_1 = 0;
const byte SNAPSHOT_2 = 1;
const byte SNAPSHOT_3 = 2;
const byte FS1_MC = 49;
const byte FS2_MC = 50;
const byte FS3_MC = 51;
const byte FS4_MC = 52;
const byte FS5_MC = 53;
const byte HX_MIDI_CHANNEL = 1;
const byte RC5_MIDI_CHANNEL = 2;
const int DEFAULT_PROGRAM = 21;
const int BOOKMARK_PROGRAM_1 = 1;
const int BOOKMARK_PROGRAM_2 = 5;
const int BOOKMARK_PROGRAM_3 = 9;
const int BOOKMARK_PROGRAM_4 = 21;
const byte CC_RC5_FCT1 = 80; //Set in the RC5 to PLAY/STOP TRACK*
const byte CC_RC5_FCT2 = 81; //Set in the RC5 to RECORD/OVERDUMP - Not yet set
const byte CC_RC5_FCT3 = 82; //Set in the RC5 to PLAY/STOP RHYTHM*
const byte CC_RC5_FCT4 = 83; //Set in the RC5 to RHYTHM VARIATION
const byte CC_RC5_FCT5 = 84; //Set in the RC5 to RHYTHM PATTERN
const byte CC_RC5_FCT6 = 85; //Set in the RC5 to CLEAR TRACK
const byte CC_RC5_FCT7 = 86; //Set in the RC5 to
const byte CC_RC5_FCT8 = 87; //Set in the RC5 to
/* Shift register constants
* ------------------------
*
* A TPIC6B595 shift register is used to control the ring LED
* Those LED are 9V control with a common ground
* I am using a TPIC6B595 and PNP diodes to deal with the common ground and 9V.
* 9V is provided directly from ON/OFF switch
*/
const uint8_t SER_DATA_Pin = 10; //DATA pin pin 14 on the TPIC6B595 pin 10 on the Arduino with blue cable
const uint8_t RCLK_LATCH_Pin = 11; //LATCH pin 12 on the TPIC6B595 pin 11 on the Arduino with white cable
const uint8_t SRCLK_SHIFT_Pin = 12; //SHIFT pin 11 on the TPIC6B595 pin 12 on the Arduino with yellow cable
const uint8_t UNASSIGNED_LED_PIN = 99;
/* Structures Definitions
* ----------------------
*/
struct MIDIStatus {
byte CC;
int Value;
byte Channel;
bool FlagOnOff;
};
struct myButton {
byte buttonNumber {1}; //Physical position on the board
bool buttonReleased {false}; // Was the button Pressed during the loop
uint8_t ShiftRegisterLEDPin[2] {UNASSIGNED_LED_PIN, UNASSIGNED_LED_PIN};
bool colorStatus[2] {LOW, LOW}; //2 color per button
long buttonPressTime;
long buttonReleaseTime;
bool BtnLongPress {false};
bool isLongPress() {
if (buttonReleaseTime - buttonPressTime > TIME_LONGPRESS) {
return true;
}
else{
return false;
}
}
};
struct myPage {
byte pageNumber {100};
byte HXmode {100};
struct myButton ListObjButton[NUM_BUTTONS];
};
/* GLOBAL VARIABLES
* ---------------
*/
byte currentPage = 1;
bool LEDHaveChanged = true;
unsigned long loopTime;
myPage ListObjPage[NUM_PAGES];
Bounce buttons[NUM_BUTTONS];
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiOut);
TimedBlink monitor(LED_BUILTIN);
byte RythmVariationIndex = 0;
//BPM assessment variables.
byte LastBtnPressed = 0;
int nunberConsecutivePress = 0;
float BPM = 60;
/* USEFUL FUNCTIONS
* ---------------- */
struct myPage & getPage(byte pageNb = currentPage) {
//This function is use so I can work with page number without dealing with the -1 for the index starting at 0
return ListObjPage[pageNb-1];
}
struct myButton & getBtn(byte BtNb, byte pageNb = currentPage) {
//This function is use so I can work with page number without dealing with the -1 for the index starting at 0
return getPage(pageNb).ListObjButton[BtNb-1];
}
void Play_Stop(byte CC){
}
void Record(){
/*The RC5 pedal does not take MIDI command to start recording
* Instead, I am using the TS jack and trigger it from the Octopus via a relay
*/
digitalWrite(RELAY_PIN,HIGH);
delay(100);
digitalWrite(RELAY_PIN,HIGH);
}
void RhythmVariation() {
int val = 0;
switch (RythmVariationIndex){
case 0:
val = 127;
RythmVariationIndex++;
break;
case 1:
val = 0;
//RythmVariationIndex++;
RythmVariationIndex=0;
break;
case 2:
val = 60;
RythmVariationIndex++;
break;
case 3:
val = 100;
RythmVariationIndex=0;
break;
};
midiOut.sendControlChange(CC_RC5_FCT4,val, RC5_MIDI_CHANNEL);
}
void RhythmPlayStop() {
midiOut.sendControlChange(CC_RC5_FCT3, 127, RC5_MIDI_CHANNEL);
midiOut.sendControlChange(CC_RC5_FCT3, 0, RC5_MIDI_CHANNEL);
}
float BPMToMS(){
//I want X beats per sec but the light will be off 1/2 of that time.
//So 1000 ms is divided by 2X beat one beat the light is on the other it is off.
float ms = 60.0*1000.0/BPM/2.0;
return ms;
}
void setBPM(){
monitor.blink(BPMToMS(),BPMToMS())
;
}
/* SCREENS FUNCTIONS
* --------------
*/
/* PRINTING FUNCTIONS
* ------------------
*/
void printBtn(byte page, byte Btn){
DEBUG_MSG.print("\tButton: ");
DEBUG_MSG.println(Btn,1);
DEBUG_MSG.print("\t\tbuttonNumber = ");
DEBUG_MSG.println(getBtn(Btn,page).buttonNumber,1);
DEBUG_MSG.print("\t\tbuttonReleased = ");
DEBUG_MSG.println(getBtn(Btn,page).buttonReleased);
DEBUG_MSG.print("\t\tLong press status = ");
DEBUG_MSG.println(getBtn(Btn,page).isLongPress());
DEBUG_MSG.print("\t\tArduinoPin = ");
DEBUG_MSG.println(BUTTON_ARDUINO_PINS[Btn-1],1);
for (byte indexColor=1; indexColor < 3; indexColor++){
DEBUG_MSG.print("\t\tColor ");
DEBUG_MSG.print(indexColor,1);
DEBUG_MSG.print(" = ");
DEBUG_MSG.println(getBtn(Btn,page).colorStatus[indexColor-1]);
DEBUG_MSG.print("\t\tColor ");
DEBUG_MSG.print(indexColor,1);
DEBUG_MSG.print(" Pin = ");
DEBUG_MSG.println(getBtn(Btn,page).ShiftRegisterLEDPin[indexColor-1],1);
}
}
void printBtns(byte page){
for (byte indexBtn = 1; indexBtn < NUM_BUTTONS + 1; indexBtn ++){
printBtn(page,indexBtn);
}
}
void printPage(byte page,bool showBtn = true){
if (DEBUG_MODE) {
DEBUG_MSG.print("Page :");
DEBUG_MSG.println(page,1);
DEBUG_MSG.print("\tpageNumber = ");
DEBUG_MSG.println(getPage(page).pageNumber,1);
DEBUG_MSG.print("\tHXmode = ");
DEBUG_MSG.println(getPage(page).HXmode,1);
if (showBtn){ printBtns(page);}
}
}
void printPages(bool showBtn) {
if (DEBUG_MODE) {
//This function print the status of a page - used for debugging.
for (int indexPg = 1; indexPg < NUM_PAGES + 1; indexPg ++){
printPage(indexPg,showBtn);
}
}
}
/* Initialization Functions
* -------------------------
*/
void initializeBtn(byte page,bool attachBtn) {
int HXMode = getPage(page).HXmode;
for (byte indexBtn = 1; indexBtn < NUM_BUTTONS + 1; indexBtn ++){
ListObjPage[page-1].ListObjButton[indexBtn-1].buttonNumber = indexBtn;
if (page ==1 and attachBtn){
//The bounce instance is the same for all the pages. So I only instantiate it once
//INPUT_PULLUP will use the internal 47k Ω resistor
buttons[indexBtn-1].attach( BUTTON_ARDUINO_PINS[indexBtn-1] , INPUT_PULLUP ); //setup the bounce instance for the current button
buttons[indexBtn-1].interval(15);
}
//The first 6 buttons have a LED with 2 color,
//I am assigning the pin on the shift register for the 2 LED
if((indexBtn-1)*2 < NUMBER_LED_RING*2 ){
ListObjPage[page-1].ListObjButton[indexBtn-1].ShiftRegisterLEDPin[0] = LED_PINS[(indexBtn-1)*2]; //color 1 pin
ListObjPage[page-1].ListObjButton[indexBtn-1].ShiftRegisterLEDPin[1] = LED_PINS[(indexBtn-1)*2+1]; //color 2 pin
}
switch (HXMode) {
case STOMP_MODE:
if (indexBtn > NUMBER_LED_RING){break;}
if (indexBtn != 6){ListObjPage[page-1].ListObjButton[indexBtn-1].colorStatus[0] = HIGH;}
break;
case SNAP_MODE:
if (indexBtn > NUMBER_LED_RING){break;}
if (indexBtn == 6){ListObjPage[page-1].ListObjButton[indexBtn-1].colorStatus[0] = HIGH;}
if (indexBtn == 1){ListObjPage[page-1].ListObjButton[indexBtn-1].colorStatus[0] = HIGH;}
break;
case SCROLL_MODE:
if (indexBtn > NUMBER_LED_RING){break;}
if (indexBtn == 1){ListObjPage[page-1].ListObjButton[indexBtn-1].colorStatus[1] = HIGH;}
if (indexBtn == 2){ListObjPage[page-1].ListObjButton[indexBtn-1].colorStatus[0] = HIGH;}
if (indexBtn == 6){ListObjPage[page-1].ListObjButton[indexBtn-1].colorStatus[0] = LOW;}
break;
}
LEDHaveChanged = true;
}
}
void initializePages() {
for (byte indexPg = 1; indexPg < NUM_PAGES + 1; indexPg ++){
ListObjPage[indexPg-1].pageNumber =indexPg;
switch (indexPg) {
case 1:
ListObjPage[indexPg-1].HXmode = SNAP_MODE;
break;
case 2:
ListObjPage[indexPg-1].HXmode = STOMP_MODE;
break;
case 3:
ListObjPage[indexPg-1].HXmode = SCROLL_MODE;
break;
case 4:
ListObjPage[indexPg-1].HXmode = SNAP_MODE;
break;
}
initializeBtn(indexPg,true);
}
}
/* LED & Shift Register Functions
* ------------------------------
*/
void setLED(byte button, byte color, bool value) {
/*Do not check if they have changed because I might have changed
* page and the reference to the btn status might not be how the LED is let.
*/
getBtn(button).colorStatus[color-1] = value;
LEDHaveChanged = true;
}
void toggleLED(byte button, byte color) {
getBtn(button).colorStatus[color-1] = not getBtn(button).colorStatus[color-1];
LEDHaveChanged = true;
}
void resetALLToLow(byte page=currentPage) {
for (int indexLED = 0; indexLED < NUMBER_LED_RING; indexLED++) {
ListObjPage[page-1].ListObjButton[indexLED].colorStatus[0] = LOW;
ListObjPage[page-1].ListObjButton[indexLED].colorStatus[1] = LOW;
}
}
void writeRegisters() {
//Output Enable (OE_bar) is wired to GND so it is always LOW
//so the output is always accessible via the latch pin
//Need to make sure this only change if something has changed.
if (LEDHaveChanged) {
digitalWrite(RCLK_LATCH_Pin, LOW); //Not outputting the data
uint8_t value = LOW;
for (byte indexPin=0; indexPin < NUM_PIN_CONTROLLED_BY_REGISTERS; indexPin++){
//The pin are from 0 to
//I can use indexPin to refer to them directly without the need for a lookup table
digitalWrite(SRCLK_SHIFT_Pin, LOW);
//Find the button for that Pin
for (byte indexBtn=1;indexBtn < NUM_BUTTONS + 1 ;indexBtn++){
if(getBtn(indexBtn).ShiftRegisterLEDPin[0] == indexPin ){
value = getBtn(indexBtn).colorStatus[0];
}
else if (getBtn(indexBtn).ShiftRegisterLEDPin[1] == indexPin){
value = getBtn(indexBtn).colorStatus[1];
}
}
digitalWrite(SER_DATA_Pin,value);
digitalWrite(SRCLK_SHIFT_Pin, HIGH); //Shift the bit
}
digitalWrite(RCLK_LATCH_Pin, HIGH); //Output to the data
LEDHaveChanged = false;
}
}
void testLEDs(){
// This function cycles the LED at the initialization of the Octopus
//Saving the current status and resetting all to LOW
bool color0InitialeState[NUMBER_LED_RING] {LOW} ;
bool color1InitialeState[NUMBER_LED_RING] {LOW} ;
for (int indexLED = 0; indexLED < NUMBER_LED_RING; indexLED++) {
color0InitialeState[indexLED] = ListObjPage[currentPage-1].ListObjButton[indexLED].colorStatus[0];
color1InitialeState[indexLED] = ListObjPage[currentPage-1].ListObjButton[indexLED].colorStatus[1];
ListObjPage[currentPage-1].ListObjButton[indexLED].colorStatus[0] = LOW;
ListObjPage[currentPage-1].ListObjButton[indexLED].colorStatus[1] = LOW;
}
LEDHaveChanged = true;
writeRegisters();
for (byte indexCol = 1; indexCol < 3;indexCol++) {
for (byte indexBtn = 1; indexBtn < NUM_BUTTONS + 1; indexBtn++){
//Skipping the btn without LED (they have a PIN set to UNASSIGNED_LED_PIN.
if(ListObjPage[currentPage-1].ListObjButton[indexBtn-1].ShiftRegisterLEDPin[0] == UNASSIGNED_LED_PIN){
continue;}
else {
setLED(indexBtn, indexCol, HIGH);
writeRegisters();
delay(200);
}
}
//Resetting all to LOW
for (int indexLED = 0; indexLED < NUMBER_LED_RING; indexLED++) {
ListObjPage[currentPage-1].ListObjButton[indexLED].colorStatus[0] = LOW;
ListObjPage[currentPage-1].ListObjButton[indexLED].colorStatus[1] = LOW;
}
LEDHaveChanged = true;
writeRegisters();
}
//Re-assigning the original status
for (int indexLED = 0; indexLED < NUMBER_LED_RING; indexLED++) {
ListObjPage[currentPage-1].ListObjButton[indexLED].colorStatus[0] = color0InitialeState[indexLED];
ListObjPage[currentPage-1].ListObjButton[indexLED].colorStatus[1] = color1InitialeState[indexLED];
}
LEDHaveChanged = true;
writeRegisters();
}
void LEDSnapshotSelection(byte btn) {
switch (btn){
case 1:
setLED(1, 1, HIGH);
setLED(1, 2, LOW);
setLED(2, 1, LOW);
setLED(2, 2, LOW);
setLED(3, 1, LOW);
setLED(3, 2, LOW);
break;
case 2:
setLED(1, 1, LOW);
setLED(1, 2, LOW);
setLED(2, 1, HIGH);
setLED(2, 2, LOW);
setLED(3, 1, LOW);
setLED(3, 2, LOW);
break;
case 3:
setLED(1, 1, LOW);
setLED(1, 2, LOW);
setLED(2, 1, LOW);
setLED(2, 2, LOW);
setLED(3, 1, HIGH);
setLED(3, 2, LOW);
break;
case 4:
toggleLED(4,1);
setLED(5,2,LOW); //In case i press play while I am recording. I need to turn off the red LED.
break;
case 6:
toggleLED(5,2);
toggleLED(4,1);
break;
}
}
void LEDBehaviorForMode(byte btn){
//When press on a btn the LED respond differently depending on the HX Mode
if (btn==6) {return;};
switch (getPage(currentPage).HXmode){
case SNAP_MODE :
LEDSnapshotSelection(btn);
writeRegisters();
break;
case STOMP_MODE :
toggleLED(btn,1);
writeRegisters();
break;
case SCROLL_MODE :
//Blinking the button when pressed
byte color = 1;
if (btn == 1) {color=2;}
for(int i=1;i < 5;i++){
toggleLED(btn,color);
writeRegisters();
delay(200);
}
break;
}
}
/* ACTIONS FOR THE BUTTONS
* ------------------------
*/
void changePage(bool isLongPress){
LastBtnPressed = 0; // BMP count is not estimated for btn pressed that occurred two different pages
if(isLongPress){
if (currentPage == 1) {
currentPage = 3;
}
else {
currentPage = 1;
}
}
else {
currentPage++;
DEBUG_MSG.print("CurrentPage = ");
DEBUG_MSG.println(currentPage);
}
if (currentPage > NUM_PAGES){
currentPage = 1;
DEBUG_MSG.print("CurrentPage = ");
DEBUG_MSG.println(currentPage);
}
for (int indexBtn = 1; indexBtn < NUM_BUTTONS + 1; indexBtn ++) {
setLED(indexBtn,1,getBtn(indexBtn).colorStatus[0]);
setLED(indexBtn,2,getBtn(indexBtn).colorStatus[1]);
}
midiOut.sendControlChange(71, getPage().HXmode, 1);
writeRegisters();
}
void changeHXProgram(byte CP) {
//When changing a program on HX, i need to reset the LEDs
midiOut.sendProgramChange(CP, HX_MIDI_CHANNEL);
for (byte indexPg = 1; indexPg < NUM_PAGES+1;indexPg++) {
resetALLToLow(indexPg);
initializeBtn(indexPg,false);
}
writeRegisters();
}
void pageOneCmd(){
//This is where all the commands for the page 1 are set.
for (byte indexBtn = 1; indexBtn < NUM_BUTTONS +1; indexBtn++){
if (not getBtn(indexBtn).buttonReleased) {
continue;}
switch (indexBtn) {
case 1:
midiOut.sendControlChange(SNAPSHOT_CC, SNAPSHOT_1, HX_MIDI_CHANNEL);
break;
case 2:
midiOut.sendControlChange(SNAPSHOT_CC, SNAPSHOT_2, HX_MIDI_CHANNEL);
break;
case 3:
midiOut.sendControlChange(SNAPSHOT_CC, SNAPSHOT_3, HX_MIDI_CHANNEL);
break;
case 4:
Play_Stop(CC_RC5_FCT1);
setLED(4, 1, HIGH);
setLED(5, 1, LOW);
setLED(5, 1, LOW);
writeRegisters();
break;
case 5:
setLED(5, 1, HIGH);
setLED(5, 1, HIGH);
Record();
writeRegisters();
break;
case 6:
changePage(getBtn(6).isLongPress());
break;
case 7:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_1);}else{
RhythmPlayStop();
}
break;
case 8:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_2);}else{
RhythmVariation();
}
break;
case 9:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_3);}else{
midiOut.sendControlChange(CC_RC5_FCT5, 0, RC5_MIDI_CHANNEL);
midiOut.sendControlChange(CC_RC5_FCT5, 127, RC5_MIDI_CHANNEL);
}
break;
case 10:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_4);}else{
midiOut.sendControlChange(CC_RC5_FCT6, 0, RC5_MIDI_CHANNEL);
midiOut.sendControlChange(CC_RC5_FCT6, 127, RC5_MIDI_CHANNEL);
}
break;
}
LEDBehaviorForMode(indexBtn);
getBtn(indexBtn).buttonReleased = false;
}
}
void pageTwoCmd(){
if (currentPage !=2) {
DEBUG_MSG.println("ERROR - ACCESSING PageTwoCmd when the current page is not 2 ");
return;
}
for (byte indexBtn = 1; indexBtn < NUM_BUTTONS +1; indexBtn++){
if (not getBtn(indexBtn).buttonReleased) {
continue;}
switch (indexBtn) {
case 1:
midiOut.sendControlChange(FS1_MC, 0, HX_MIDI_CHANNEL);
break;
case 2:
midiOut.sendControlChange(FS2_MC, 0, HX_MIDI_CHANNEL);
break;
case 3:
midiOut.sendControlChange(FS3_MC, 0, HX_MIDI_CHANNEL);
break;
case 4:
midiOut.sendControlChange(FS4_MC, 0, HX_MIDI_CHANNEL);
break;
case 5:
midiOut.sendControlChange(FS5_MC, 0, HX_MIDI_CHANNEL);
break;
case 6:
changePage(getBtn(6).isLongPress());
break;
case 7:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_1);}else{
Keyboard.write('k');
}
break;
case 8:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_2);}else{
Keyboard.write('0');
}
break;
case 9:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_3);}else{
Keyboard.write('j');
}
break;
case 10:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_4);}else{
}
break;
}
LEDBehaviorForMode(indexBtn);
getBtn(indexBtn).buttonReleased = false;
}
}
void pageThreeCmd(){
if (currentPage !=3) {
DEBUG_MSG.print("ERROR - ACCESSING PageThreeCmd when the current page is not 3 ");
return;
}
for (byte indexBtn = 1; indexBtn < NUM_BUTTONS +1; indexBtn++){
if (not getBtn(indexBtn).buttonReleased) {continue;}
switch (indexBtn) {
case 1:
midiOut.sendControlChange(FS1_MC, 0, HX_MIDI_CHANNEL);
break;
case 2:
midiOut.sendControlChange(FS2_MC, 0, HX_MIDI_CHANNEL);
break;
case 3:
setBPM();
//Send the tap to the HX (I not sending the bpm byt instead a tap so the hX can do its own BPM calculation
//BPM IS ONLY USE HERE TO DRIVE THE led / screen
midiOut.sendControlChange(64, 127, HX_MIDI_CHANNEL);
break;
case 4:
break;
case 5:
break;
case 6:
changePage(getBtn(6).isLongPress());
break;
case 7:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_1);}else{
//RC5 Play/Stop Rhythm (apparently 0 and 127 are needed ?)
midiOut.sendControlChange(CC_RC5_FCT2, 0, RC5_MIDI_CHANNEL);
midiOut.sendControlChange(CC_RC5_FCT2, 127, RC5_MIDI_CHANNEL);
}
break;
case 8:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_2);}else{
RhythmVariation();
}
break;
case 9:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_3);}else{
}
break;
case 10:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_4);}else{
}
break;
}
LEDBehaviorForMode(indexBtn);
getBtn(indexBtn).buttonReleased = false;
}
}
void pageFourCmd(){
//This is where all the commands for the page 4 are set.
for (byte indexBtn = 1; indexBtn < NUM_BUTTONS +1; indexBtn++){
if (not getBtn(indexBtn).buttonReleased) {
continue;}
switch (indexBtn) {
case 1:
midiOut.sendControlChange(SNAPSHOT_CC, SNAPSHOT_1, HX_MIDI_CHANNEL);
break;
case 2:
midiOut.sendControlChange(SNAPSHOT_CC, SNAPSHOT_2, HX_MIDI_CHANNEL);
break;
case 3:
midiOut.sendControlChange(SNAPSHOT_CC, SNAPSHOT_3, HX_MIDI_CHANNEL);
break;
case 4:
break;
case 5:
break;
case 6:
changePage(getBtn(6).isLongPress());
break;
case 7:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_1);}else{
//RC5 Play/Stop Rhythm (apparently 0 and 127 are needed ?)
midiOut.sendControlChange(CC_RC5_FCT2, 0, RC5_MIDI_CHANNEL);
midiOut.sendControlChange(CC_RC5_FCT2, 127, RC5_MIDI_CHANNEL);
}
break;
case 8:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_2);}else{
//YouTube restart
Keyboard.write('0'); }
break;
case 9:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_3);}else{
//YouTube play/stop
Keyboard.write('k');
}
break;
case 10:
if (getBtn(indexBtn).isLongPress()){ changeHXProgram(BOOKMARK_PROGRAM_4);}else{
//YouTube rewind 5sec
Keyboard.write('j');
}
break;
}
LEDBehaviorForMode(indexBtn);
getBtn(indexBtn).buttonReleased = false;
}
}
/* BUTTONS RELATED FUNCTIONS
* -------------------------
*/
void readButtons(unsigned long time) {
//Read the button and see which ones are pressed.
for (byte indexBtn = 1; indexBtn < NUM_BUTTONS + 1; indexBtn ++) {
getBtn(indexBtn).buttonReleased= false;
buttons[indexBtn-1].update();
if (buttons[indexBtn-1].fell()) {
/* BPM determination
* I look if the same btn is pressed multiple time in short intervals
* in order to count the desired BPM.
* If there is too much time between pressed, I reset the count
* I limit BPM between 30 and 250 which correspond to
* one beat every 2000 ms to one beat every 240 ms.
* ms per beat = BPM/60000
* 1 -> 2000 ms
* 1/2000*60*1000
* BPM is not sent to the HX until the executeBtn() function is instructed to do so.
* Here it is simply counting what it could be without further actions.
*/
if (indexBtn == LastBtnPressed and time <= getBtn(indexBtn).buttonPressTime + 2000){
//Quick multi-press detected. This could be a BPM tap button
nunberConsecutivePress = nunberConsecutivePress + 1;
long TimeBetweenPressed = time - getBtn(indexBtn).buttonPressTime;
long instantBPM = 60.0*1000.0/TimeBetweenPressed; //Non averaged BPM
if (nunberConsecutivePress == 2) { BPM = instantBPM;}
else {
BPM = ((nunberConsecutivePress-1) * BPM + instantBPM)/(nunberConsecutivePress);
}
DEBUG_MSG.print("Instant BPM ");
DEBUG_MSG.println(instantBPM);
DEBUG_MSG.print("BPM ");
DEBUG_MSG.println(BPM);
DEBUG_MSG.print("TimeBetweenPressed ");
DEBUG_MSG.println(TimeBetweenPressed);
DEBUG_MSG.print("Nb Pressed ");
DEBUG_MSG.println(nunberConsecutivePress);
}
else {
LastBtnPressed = indexBtn;
nunberConsecutivePress = 1;
}
getBtn(indexBtn).buttonPressTime = time;
// DEBUG_MSG.print("Btn ");
// DEBUG_MSG.print(indexBtn,1);
// DEBUG_MSG.println("was pressed ");
}
if (buttons[indexBtn-1].rose()) {
getBtn(indexBtn).buttonReleaseTime = time;
getBtn(indexBtn).buttonReleased= true;
// DEBUG_MSG.print("Btn ");
// DEBUG_MSG.print(indexBtn,1);
// DEBUG_MSG.println("was released ");
// printBtn(currentPage,indexBtn);
}
}
}
void executeBtn(){
switch (currentPage) {
case 1:
pageOneCmd();
break;
case 2:
pageTwoCmd();
break;
case 3:
pageThreeCmd();
break;
case 4:
pageFourCmd();
break;
}
}
/* SETUP AND LOOP
* ---------------
*/
void setup() {
//Using the Serial for debug messages.
if (DEBUG_MODE) {
Serial.begin(9600);
while (!SerialUSB){
; //Wait for the serial to connect.
}
}
//BPM is kept in memory even if the arduino is shut-off
byte BPM_stored;
EEPROM.get(0, BPM_stored);
if (isnan(BPM_stored))
{
DEBUG_MSG.println("BPM is not initialized.");
DEBUG_MSG.println("Initializing BPM to 60");
EEPROM.put(0, BPM);
}
DEBUG_MSG.print("Debug MODE ");
pinMode(LED_BUILTIN, OUTPUT);
monitor.blink(BPMToMS(),BPMToMS());//LED on for 150 ms and off for 50 ms.
pinMode(SER_DATA_Pin, OUTPUT);
pinMode(RCLK_LATCH_Pin, OUTPUT);
pinMode(SRCLK_SHIFT_Pin, OUTPUT);
initializePages();
testLEDs();
Keyboard.begin();
Serial1.begin(31250);
midiOut.sendProgramChange(DEFAULT_PROGRAM, HX_MIDI_CHANNEL);
midiOut.sendControlChange(71,SNAP_MODE, HX_MIDI_CHANNEL);
midiOut.sendControlChange(SNAPSHOT_CC, SNAPSHOT_1, HX_MIDI_CHANNEL);
DEBUG_MSG.println("Done with Initialization");
// monitor.blink(500,250);
}
void loop() {
monitor.blink(BPMToMS(),BPMToMS());
readButtons(millis());
executeBtn();
}