about summary refs log tree commit diff
path: root/HexBoard_V1.1.ino
diff options
context:
space:
mode:
authorZach DeCook <zachdecook@librem.one>2022-12-16 18:47:22 -0500
committerZach DeCook <zachdecook@librem.one>2022-12-16 18:47:22 -0500
commitaa126580f6b1a3281378c495754f8f8be97f48c1 (patch)
treea6af52d36e19d1b550e72fa9df51f98ed57c0596 /HexBoard_V1.1.ino
parent37cb602acfc6e28dbd163071ecc77d1d362ab1f8 (diff)
downloadHexBoard-aa126580f6b1a3281378c495754f8f8be97f48c1.tar.gz
v1.1: un-refactor
Diffstat (limited to 'HexBoard_V1.1.ino')
-rw-r--r--HexBoard_V1.1.ino516
1 files changed, 428 insertions, 88 deletions
diff --git a/HexBoard_V1.1.ino b/HexBoard_V1.1.ino
index 6c5b3fd..7265bf3 100644
--- a/HexBoard_V1.1.ino
+++ b/HexBoard_V1.1.ino
@@ -1,25 +1,48 @@
 // Hardware Information:
 // Generic RP2040 running at 133MHz with 16MB of flash
+// https://github.com/earlephilhower/arduino-pico
 
 // Brilliant resource for dealing with hexagonal coordinates. https://www.redblobgames.com/grids/hexagons/
 // Might be useful for animations and stuff like that.
 
+// Menu library documentation https://github.com/Spirik/GEM
+
 #include <Arduino.h>
 #include <Adafruit_TinyUSB.h>
 #include <MIDI.h>
 #include <Adafruit_NeoPixel.h>
+#define GEM_DISABLE_GLCD
+#include <GEM_u8g2.h>
+#include <Wire.h>
+#include <Rotary.h>
 
-// USB MIDI object
+// USB MIDI object //
 Adafruit_USBD_MIDI usb_midi;
-
 // Create a new instance of the Arduino MIDI Library,
 // and attach usb_midi as the transport.
 MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI);
 
+// LED SETUP //
 #define LED_PIN 22
 #define LED_COUNT 140
-
 Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_RGB + NEO_KHZ800);
+int defaultBrightness = 50;
+int dimBrightness = 15;
+int pressedBrightness = 180;
+
+
+// ENCODER SETUP //
+#define ROTA 20  // Rotary encoder A
+#define ROTB 21  // Rotary encoder B
+Rotary rotary = Rotary(ROTA, ROTB);
+const int encoderClick = 24;
+int encoderState = 0;
+int encoderLastState = 1;
+int8_t encoder_val = 0;
+uint8_t encoder_state;
+
+// Create an instance of the U8g2 graphics library.
+U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/U8X8_PIN_NONE);
 
 //
 // Button matrix and LED locations
@@ -39,13 +62,13 @@ Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_RGB + NEO_KHZ800);
 //  130     129 128 127 126 125 124 123 122 121
 //        140 139 138 137 136 135 134 133 132 131
 
-// DIAGNOSTICS
+// DIAGNOSTICS //
 // 1 = Full button test (1 and 0)
 // 2 = Button test (button number)
 // 3 = MIDI output test
 int diagnostics = 0;
 
-// Define digital button matrix pins
+// BUTTON MATRIX PINS //
 const byte columns[] = { 14, 15, 13, 12, 11, 10, 9, 8, 7, 6 };  // Column pins in order from right to left
 const int m1p = 4;                                              // Multiplexing chip control pins
 const int m2p = 5;
@@ -58,77 +81,81 @@ const byte elementCount = columnCount * rowCount;  // The number of elements in
 
 // Since MIDI only uses 7 bits, we can give greater values special meanings.
 // (see commandPress)
-const int OCT_DN = 128;
-const int OCT_UP = 129;
-const int BT_TOG = 130;
-const int LAY_MD = 131;
-const int LGH_MD = 132;
-const int PTB_DN = 133;
-const int PTB_UP = 134;
-const int BK_TOG = 135;
+const int CMDB_1 = 128;
+const int CMDB_2 = 129;
+const int CMDB_3 = 130;
+const int CMDB_4 = 131;
+const int CMDB_5 = 132;
+const int CMDB_6 = 133;
+const int CMDB_7 = 134;
 const int UNUSED = 255;
 
+// LED addresses for CMD buttons.
+const byte cmdBtn1 = 10 - 1;
+const byte cmdBtn2 = 30 - 1;
+const byte cmdBtn3 = 50 - 1;
+const byte cmdBtn4 = 70 - 1;
+const byte cmdBtn5 = 90 - 1;
+const byte cmdBtn6 = 110 - 1;
+const byte cmdBtn7 = 130 - 1;
+
+// MIDI NOTE LAYOUTS //
 #define ROW_FLIP(x, ix, viii, vii, vi, v, iv, iii, ii, i) i, ii, iii, iv, v, vi, vii, viii, ix, x
 //hacky macro because I (Jared) messed up the board layout - I'll do better next time! xD
 
-// MIDI note value tables
+// MIDI note layout tables
 const byte wickiHaydenLayout[elementCount] = {
-  ROW_FLIP(BK_TOG, 90, 92, 94, 96, 98, 100, 102, 104, 106),
+  ROW_FLIP(CMDB_1, 90, 92, 94, 96, 98, 100, 102, 104, 106),
   ROW_FLIP(83, 85, 87, 89, 91, 93, 95, 97, 99, 101),
-  ROW_FLIP(LGH_MD, 78, 80, 82, 84, 86, 88, 90, 92, 94),
+  ROW_FLIP(CMDB_2, 78, 80, 82, 84, 86, 88, 90, 92, 94),
   ROW_FLIP(71, 73, 75, 77, 79, 81, 83, 85, 87, 89),
-  ROW_FLIP(LAY_MD, 66, 68, 70, 72, 74, 76, 78, 80, 82),
+  ROW_FLIP(CMDB_3, 66, 68, 70, 72, 74, 76, 78, 80, 82),
   ROW_FLIP(59, 61, 63, 65, 67, 69, 71, 73, 75, 77),
-  ROW_FLIP(OCT_UP, 54, 56, 58, 60, 62, 64, 66, 68, 70),
+  ROW_FLIP(CMDB_4, 54, 56, 58, 60, 62, 64, 66, 68, 70),
   ROW_FLIP(47, 49, 51, 53, 55, 57, 59, 61, 63, 65),
-  ROW_FLIP(OCT_DN, 42, 44, 46, 48, 50, 52, 54, 56, 58),
+  ROW_FLIP(CMDB_5, 42, 44, 46, 48, 50, 52, 54, 56, 58),
   ROW_FLIP(35, 37, 39, 41, 43, 45, 47, 49, 51, 53),
-  ROW_FLIP(PTB_UP, 30, 32, 34, 36, 38, 40, 42, 44, 46),
+  ROW_FLIP(CMDB_6, 30, 32, 34, 36, 38, 40, 42, 44, 46),
   ROW_FLIP(23, 25, 27, 29, 31, 33, 35, 37, 39, 41),
-  ROW_FLIP(PTB_DN, 18, 20, 22, 24, 26, 28, 30, 32, 34),
+  ROW_FLIP(CMDB_7, 18, 20, 22, 24, 26, 28, 30, 32, 34),
   ROW_FLIP(11, 13, 15, 17, 19, 21, 23, 25, 27, 29)
 };
 const byte harmonicTableLayout[elementCount] = {
-  ROW_FLIP(BK_TOG, 83, 76, 69, 62, 55, 48, 41, 34, 27),
+  ROW_FLIP(CMDB_1, 83, 76, 69, 62, 55, 48, 41, 34, 27),
   ROW_FLIP(86, 79, 72, 65, 58, 51, 44, 37, 30, 23),
-  ROW_FLIP(LGH_MD, 82, 75, 68, 61, 54, 47, 40, 33, 26),
+  ROW_FLIP(CMDB_2, 82, 75, 68, 61, 54, 47, 40, 33, 26),
   ROW_FLIP(85, 78, 71, 64, 57, 50, 43, 36, 29, 22),
-  ROW_FLIP(LAY_MD, 81, 74, 67, 60, 53, 46, 39, 32, 25),
+  ROW_FLIP(CMDB_3, 81, 74, 67, 60, 53, 46, 39, 32, 25),
   ROW_FLIP(84, 77, 70, 63, 56, 49, 42, 35, 28, 21),
-  ROW_FLIP(OCT_UP, 80, 73, 66, 59, 52, 45, 38, 31, 24),
+  ROW_FLIP(CMDB_4, 80, 73, 66, 59, 52, 45, 38, 31, 24),
   ROW_FLIP(83, 76, 69, 62, 55, 48, 41, 34, 27, 20),
-  ROW_FLIP(OCT_DN, 79, 72, 65, 58, 51, 44, 37, 30, 23),
+  ROW_FLIP(CMDB_5, 79, 72, 65, 58, 51, 44, 37, 30, 23),
   ROW_FLIP(82, 75, 68, 61, 54, 47, 40, 33, 26, 19),
-  ROW_FLIP(PTB_UP, 78, 71, 64, 57, 50, 43, 36, 29, 22),
+  ROW_FLIP(CMDB_6, 78, 71, 64, 57, 50, 43, 36, 29, 22),
   ROW_FLIP(81, 74, 67, 60, 53, 46, 39, 32, 25, 18),
-  ROW_FLIP(PTB_DN, 77, 70, 63, 56, 49, 42, 35, 28, 21),
+  ROW_FLIP(CMDB_7, 77, 70, 63, 56, 49, 42, 35, 28, 21),
   ROW_FLIP(80, 73, 66, 59, 52, 45, 38, 31, 24, 17)
 };
 const byte gerhardLayout[elementCount] = {
-  ROW_FLIP(BK_TOG, 74, 73, 72, 71, 70, 69, 68, 67, 66),
+  ROW_FLIP(CMDB_1, 74, 73, 72, 71, 70, 69, 68, 67, 66),
   ROW_FLIP(71, 70, 69, 68, 67, 66, 65, 64, 63, 62),
-  ROW_FLIP(LGH_MD, 67, 66, 65, 64, 63, 62, 61, 60, 59),
+  ROW_FLIP(CMDB_2, 67, 66, 65, 64, 63, 62, 61, 60, 59),
   ROW_FLIP(64, 63, 62, 61, 60, 59, 58, 57, 56, 55),
-  ROW_FLIP(LAY_MD, 60, 59, 58, 57, 56, 55, 54, 53, 52),
+  ROW_FLIP(CMDB_3, 60, 59, 58, 57, 56, 55, 54, 53, 52),
   ROW_FLIP(57, 56, 55, 54, 53, 52, 51, 50, 49, 48),
-  ROW_FLIP(OCT_UP, 53, 52, 51, 50, 49, 48, 47, 46, 45),
+  ROW_FLIP(CMDB_4, 53, 52, 51, 50, 49, 48, 47, 46, 45),
   ROW_FLIP(50, 49, 48, 47, 46, 45, 44, 43, 42, 41),
-  ROW_FLIP(OCT_DN, 46, 45, 44, 43, 42, 41, 40, 39, 38),
+  ROW_FLIP(CMDB_5, 46, 45, 44, 43, 42, 41, 40, 39, 38),
   ROW_FLIP(43, 42, 41, 40, 39, 38, 37, 36, 35, 34),
-  ROW_FLIP(PTB_UP, 39, 38, 37, 36, 35, 34, 33, 32, 31),
+  ROW_FLIP(CMDB_6, 39, 38, 37, 36, 35, 34, 33, 32, 31),
   ROW_FLIP(36, 35, 34, 33, 32, 31, 30, 29, 28, 27),
-  ROW_FLIP(PTB_DN, 32, 31, 30, 29, 28, 27, 26, 25, 24),
+  ROW_FLIP(CMDB_7, 32, 31, 30, 29, 28, 27, 26, 25, 24),
   ROW_FLIP(29, 28, 27, 26, 25, 24, 23, 22, 21, 20)
 };
-// LEDs for OCT_UP/OCT_DN status.
-const byte octUpSW = 70 - 1;
-const byte octDnSW = 90 - 1;
-const byte layMdSW = 50 - 1;
-
 const byte *currentLayout = wickiHaydenLayout;
 
 // Global time variables
-unsigned long currentTime;  // Program loop consistent variable for time in milliseconds since power on
+unsigned long currentTime;    // Program loop consistent variable for time in milliseconds since power on
 const byte debounceTime = 2;  // Global digital button debounce time in milliseconds
 
 // Variables for holding digital button states and activation times
@@ -136,34 +163,77 @@ byte activeButtons[elementCount];               // Array to hold current note bu
 byte previousActiveButtons[elementCount];       // Array to hold previous note button states for comparison
 unsigned long activeButtonsTime[elementCount];  // Array to track last note button activation time for debounce
 
+// MENU SYSTEM SETUP //
+// Create menu page object of class GEMPage. Menu page holds menu items (GEMItem) and represents menu level.
+// Menu can have multiple menu pages (linked to each other) with multiple menu items each
+GEMPage menuPageMain("HexBoard MIDI Controller");
+GEMPage menuPageLayout("Layout");
+
+GEMItem menuItemLayout("Layout", menuPageLayout);
+void wickiHayden();  //Forward declarations
+void harmonicTable();
+void gerhard();
+GEMItem menuItemWickiHayden("Wicki-Hayden", wickiHayden);
+GEMItem menuItemHarmonicTable("Harmonic Table", harmonicTable);
+GEMItem menuItemGerhard("Gerhard", gerhard);
+
+void setLayoutLEDs();  //Forward declaration
+byte key = 0;
+SelectOptionByte selectKeyOptions[] = { { "C", 0 }, { "C#", 1 }, { "D", 2 }, { "D#", 3 }, { "E", 4 }, { "F", 5 }, { "F#", 6 }, { "G", 7 }, { "G#", 8 }, { "A", 9 }, { "A#", 10 }, { "B", 11 } };
+GEMSelect selectKey(sizeof(selectKeyOptions) / sizeof(SelectOptionByte), selectKeyOptions);
+GEMItem menuItemKey("Key:", key, selectKey, setLayoutLEDs);
+
+byte scale = 0;
+SelectOptionByte selectScaleOptions[] = { { "NONE", 0 }, { "Major", 1 }, { "HarMin", 2 }, { "MelMin", 3 }, { "NatMin", 4 }, { "NONE", 5 }, { "NONE", 6 }, { "NONE", 7 }, { "NONE", 8 }, { "NONE", 9 }, { "NONE", 10 }, { "NONE", 11 } };
+GEMSelect selectScale(sizeof(selectScaleOptions) / sizeof(SelectOptionByte), selectScaleOptions);
+GEMItem menuItemScale("Scale:", scale, selectScale, setLayoutLEDs);
+
+int transpose = 0;
+SelectOptionInt selectTransposeOptions[] = {
+  { "-12", -12 }, { "-11", -11 }, { "-10", -10 }, { "-9", -9 }, { "-8", -8 }, { "-7", -7 }, { "-6", -6 }, { "-5", -5 }, { "-4", -4 }, { "-3", -3 }, { "-2", -2 }, { "-1", -1 }, { "0", 0 }, { "+1", 1 }, { "+2", 2 }, { "+3", 3 }, { "+4", 4 }, { "+5", 5 }, { "+6", 6 }, { "+7", 7 }, { "+8", 8 }, { "+9", 9 }, { "+10", 10 }, { "+11", 11 }, { "+12", 12 }
+};
+GEMSelect selectTranspose(sizeof(selectTransposeOptions) / sizeof(SelectOptionByte), selectTransposeOptions);
+void validateTranspose();  // Forward declaration
+GEMItem menuItemTranspose("Transpose:", transpose, selectTranspose, validateTranspose);
+
+//bool highlightScale = true;  // whether the black keys should be dimmer  REMOVING THIS SOON
+//GEMItem menuItemHighlightScale("Scale Light:", highlightScale, setLayoutLEDs);
+
+
+
+// Create menu object of class GEM_u8g2. Supply its constructor with reference to u8g2 object we created earlier
+byte menuItemHeight = 10;
+byte menuPageScreenTopOffset = 10;
+byte menuValuesLeftOffset = 86;
+GEM_u8g2 menu(u8g2, GEM_POINTER_ROW, GEM_ITEMS_COUNT_AUTO, menuItemHeight, menuPageScreenTopOffset, menuValuesLeftOffset);
+
 
 // MIDI channel assignment
 byte midiChannel = 1;  // Current MIDI channel (changed via user input)
 
-// Octave modifier
-int octave = 0;  // Apply a MIDI note number offset (changed via user input in steps of 12)
-
-bool blackKeys = true;  // whether the black keys should be dimmer
 
 // Velocity levels
-byte midiVelocity = 95;  // Default velocity
+byte midiVelocity = 100;  // Default velocity
 // END SETUP SECTION
 // ------------------------------------------------------------------------------------------------------------------------------------------------------------
 
 void setup() {
+
 #if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
   // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
   TinyUSB_Device_Init(0);
 #endif
-
-  //usb_midi.setStringDescriptor("HexBoard MIDI");
-
+  usb_midi.setStringDescriptor("HexBoard MIDI");
   // Initialize MIDI, and listen to all MIDI channels
   // This will also call usb_midi's begin()
   MIDI.begin(MIDI_CHANNEL_OMNI);
 
-  // Set serial to make uploads work without bootsel button
-  Serial.begin(115200);
+  Wire.setSDA(16);
+  Wire.setSCL(17);
+
+  pinMode(encoderClick, INPUT_PULLUP);
+
+  Serial.begin(115200);  // Set serial to make uploads work without bootsel button
 
   // Set pinModes for the digital button matrix.
   for (int pinNumber = 0; pinNumber < columnCount; pinNumber++)  // For each column pin...
@@ -174,13 +244,18 @@ void setup() {
   pinMode(m2p, OUTPUT);
   pinMode(m4p, OUTPUT);
   pinMode(m8p, OUTPUT);
-  Serial.begin(115200);
-  strip.begin();             // INITIALIZE NeoPixel strip object (REQUIRED)
+
+  strip.begin();             // INITIALIZE NeoPixel strip object
   strip.show();              // Turn OFF all pixels ASAP
-  strip.setBrightness(128);  // Set BRIGHTNESS (max = 255)
-  setOctLED();
+  strip.setBrightness(255);  // Set BRIGHTNESS (max = 255)
+  setCMD_LEDs();
+  strip.setPixelColor(cmdBtn1, strip.ColorHSV(65536 / 12, 255, defaultBrightness));
   setLayoutLEDs();
-  strip.setPixelColor(layMdSW, 255, 0, 0);
+
+  u8g2.begin();  //Menu and graphics setup
+  menu.init();
+  setupMenu();
+  menu.drawMenu();
 
   // wait until device mounted
   while (!TinyUSBDevice.mounted()) delay(1);
@@ -189,6 +264,12 @@ void setup() {
   diagnosticTest();
 }
 
+void setup1() {  //Second core exclusively runs encoder
+  //pinMode(ROTA, INPUT_PULLUP);
+  //pinMode(ROTB, INPUT_PULLUP);
+  //encoder_init();
+}
+
 // ------------------------------------------------------------------------------------------------------------------------------------------------------------
 // START LOOP SECTION
 void loop() {
@@ -201,7 +282,7 @@ void loop() {
   // Act on those buttons
   playNotes();
 
-  // Held Buttons
+  // Held buttons
   heldButtons();
 
   // Do the LEDS
@@ -209,6 +290,28 @@ void loop() {
 
   // Read any new MIDI messages
   MIDI.read();
+
+  // Read menu functions
+  if (menu.readyForKey()) {
+    encoderState = digitalRead(encoderClick);
+    if (encoderState > encoderLastState) {
+      menu.registerKeyPress(GEM_KEY_OK);
+    }
+    encoderLastState = encoderState;
+    if (encoder_val < 0) {
+      menu.registerKeyPress(GEM_KEY_UP);
+      encoder_val = 0;
+    }
+    if (encoder_val > 0) {
+      menu.registerKeyPress(GEM_KEY_DOWN);
+      encoder_val = 0;
+    }
+  }
+}
+
+void loop1() {
+  rotate();
+  //readEncoder();
 }
 // END LOOP SECTION
 // ------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -224,43 +327,280 @@ void diagnosticTest() {
 }
 
 void commandPress(byte command) {
-  // Keep octave between ~-12~ 0 and 24
-  if (command == OCT_DN) {
-    if (octave >= 12) {
-      octave -= 12;
-      setOctLED();
-      strip.setPixelColor(octDnSW, 120, 120, 120);
+  if (command == CMDB_1) {
+    midiVelocity = 100;
+    setCMD_LEDs();
+    strip.setPixelColor(cmdBtn1, strip.ColorHSV(65536 / 12, 255, defaultBrightness));
+  }
+  if (command == CMDB_2) {
+    midiVelocity = 60;
+    setCMD_LEDs();
+    strip.setPixelColor(cmdBtn2, strip.ColorHSV(65536 / 3, 255, defaultBrightness));
+  }
+  if (command == CMDB_3) {
+    midiVelocity = 20;
+    setCMD_LEDs();
+    strip.setPixelColor(cmdBtn3, strip.ColorHSV(65536 / 2, 255, defaultBrightness));
+  }
+  if (command == CMDB_4) {
+  }
+  if (command == CMDB_5) {
+  }
+  if (command == CMDB_6) {
+  }
+  if (command == CMDB_7) {
+  }
+}
+void commandRelease(byte command) {
+}
+
+// BUTTONS //
+void readDigitalButtons() {
+  if (diagnostics == 1) {
+    Serial.println();
+  }
+  // Button Deck
+  for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)  // Iterate through each of the row pins on the multiplexing chip.
+  {
+    digitalWrite(m1p, rowIndex & 1);
+    digitalWrite(m2p, (rowIndex & 2) >> 1);
+    digitalWrite(m4p, (rowIndex & 4) >> 2);
+    digitalWrite(m8p, (rowIndex & 8) >> 3);
+    for (byte columnIndex = 0; columnIndex < columnCount; columnIndex++)  // Now iterate through each of the column pins that are connected to the current row pin.
+    {
+      byte columnPin = columns[columnIndex];                              // Hold the currently selected column pin in a variable.
+      pinMode(columnPin, INPUT_PULLUP);                                   // Set that row pin to INPUT_PULLUP mode (+3.3V / HIGH).
+      byte buttonNumber = columnIndex + (rowIndex * columnCount);         // Assign this location in the matrix a unique number.
+      delayMicroseconds(10);                                              // Delay to give the pin modes time to change state (false readings are caused otherwise).
+      previousActiveButtons[buttonNumber] = activeButtons[buttonNumber];  // Track the "previous" variable for comparison.
+      byte buttonState = digitalRead(columnPin);                          // (don't)Invert reading due to INPUT_PULLUP, and store the currently selected pin state.
+      if (buttonState == LOW) {
+        if (diagnostics == 1) {
+          Serial.print("1");
+        } else if (diagnostics == 2) {
+          Serial.println(buttonNumber);
+        }
+        if (!previousActiveButtons[buttonNumber]) {
+          // newpress time
+          activeButtonsTime[buttonNumber] = millis();
+        }
+        // TODO: Implement debounce?
+        activeButtons[buttonNumber] = 1;
+      } else {
+        // Otherwise, the button is inactive, write a 0.
+        if (diagnostics == 1) {
+          Serial.print("0");
+        }
+        activeButtons[buttonNumber] = 0;
+      }
+      // Set the selected column pin back to INPUT mode (0V / LOW).
+      pinMode(columnPin, INPUT);
+    }
+  }
+}
+
+void playNotes() {
+  for (int i = 0; i < elementCount; i++)  // For all buttons in the deck
+  {
+    if (activeButtons[i] != previousActiveButtons[i])  // If a change is detected
+    {
+      if (activeButtons[i] == 1)  // If the button is active (newpress)
+      {
+        if (currentLayout[i] < 128) {
+          strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, pressedBrightness));
+          noteOn(midiChannel, (currentLayout[i] + transpose) % 128, midiVelocity);
+        } else {
+          commandPress(currentLayout[i]);
+        }
+      } else {
+        // If the button is inactive (released)
+        if (currentLayout[i] < 128) {
+          setLayoutLED(i);
+          noteOff(midiChannel, (currentLayout[i] + transpose) % 128, 0);
+        } else {
+          commandRelease(currentLayout[i]);
+        }
+      }
     }
-  } else if (command == OCT_UP) {
-    if (octave <= 12) {
-      octave += 12;
-      setOctLED();
-      strip.setPixelColor(octUpSW, 120, 120, 120);
+  }
+}
+
+void heldButtons() {
+  for (int i = 0; i < elementCount; i++) {
+    if (activeButtons[i]) {
+      //if (
     }
-  } else if (command == LAY_MD) {
-    if (currentLayout == wickiHaydenLayout) {
-      currentLayout = harmonicTableLayout;
-    } else if (currentLayout == harmonicTableLayout) {
-      currentLayout = gerhardLayout;
-    } else {
-      currentLayout = wickiHaydenLayout;
+  }
+}
+
+// MIDI AND OTHER OUTPUTS //
+// Send Note On
+void noteOn(byte channel, byte pitch, byte velocity) {
+  MIDI.sendNoteOn(pitch, velocity, channel);
+  if (diagnostics == 3) {
+    Serial.print(pitch);
+    Serial.print(", ");
+    Serial.print(velocity);
+    Serial.print(", ");
+    Serial.println(channel);
+  }
+}
+// Send Note Off
+void noteOff(byte channel, byte pitch, byte velocity) {
+  MIDI.sendNoteOff(pitch, velocity, channel);
+}
+
+// LEDS //
+void setCMD_LEDs() {
+  strip.setPixelColor(cmdBtn1, strip.ColorHSV(65536 / 12, 255, dimBrightness));
+  strip.setPixelColor(cmdBtn2, strip.ColorHSV(65536 / 3, 255, dimBrightness));
+  strip.setPixelColor(cmdBtn3, strip.ColorHSV(65536 / 2, 255, dimBrightness));
+  strip.setPixelColor(cmdBtn4, strip.ColorHSV(0, 255, defaultBrightness));
+  strip.setPixelColor(cmdBtn5, strip.ColorHSV(0, 255, defaultBrightness));
+  strip.setPixelColor(cmdBtn6, strip.ColorHSV(0, 255, defaultBrightness));
+  strip.setPixelColor(cmdBtn7, strip.ColorHSV(0, 255, defaultBrightness));
+}
+
+void setLayoutLEDs() {
+  for (int i = 0; i < elementCount; i++) {
+    if (currentLayout[i] <= 127) {
+      setLayoutLED(i);
     }
-    setLayoutLEDs();
-  } else if (command == BK_TOG) {
-    blackKeys = !blackKeys;
-    setLayoutLEDs();
   }
 }
-void commandRelease(byte command) {
-  if (command == OCT_DN) {
-    setOctLED();
-  } else if (command == OCT_UP) {
-    setOctLED();
+void setLayoutLED(int i) {
+  strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, defaultBrightness));
+  // Scale highlighting
+  if (scale == 0) {  //NONE
+    switch ((currentLayout[i] - key + transpose) % 12) {
+      default: break;  // No changes since there is no scale selected
+    }
+  }
+  if (scale == 1) {  //Major
+    switch ((currentLayout[i] - key + transpose) % 12) {
+      // If it is one of the dark keys, fall through to case 10.
+      case 1:
+      case 3:
+      case 6:
+      case 8:
+      case 10: strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, dimBrightness)); break;
+      // Otherwise it was a highlighted key. Do nothing
+      default: break;
+    }
+  }
+  if (scale == 2) {  //HarMin
+    switch ((currentLayout[i] - key + transpose) % 12) {
+      // If it is one of the dark keys, fall through to case 10.
+      case 1:
+      case 4:
+      case 6:
+      case 9:
+      case 10: strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, dimBrightness)); break;
+      // Otherwise it was a highlighted key. Do nothing
+      default: break;
+    }
+  }
+  if (scale == 3) {  //MelMin
+    switch ((currentLayout[i] - key + transpose) % 12) {
+      // If it is one of the dark keys, fall through to case 10.
+      case 1:
+      case 4:
+      case 6:
+      case 8:
+      case 10: strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, dimBrightness)); break;
+      // Otherwise it was a highlighted key. Do nothing
+      default: break;
+    }
+  }
+  if (scale == 4) {  //NatMin
+    switch ((currentLayout[i] - key + transpose) % 12) {
+      // If it is one of the dark keys, fall through to case 10.
+      case 1:
+      case 4:
+      case 6:
+      case 9:
+      case 11: strip.setPixelColor(i, strip.ColorHSV(((currentLayout[i] - key + transpose) % 12) * 5006, 255, dimBrightness)); break;
+      // Otherwise it was a highlighted key. Do nothing
+      default: break;
+    }
   }
 }
 
-// END FUNCTIONS SECTION
-// ------------------------------------------------------------------------------------------------------------------------------------------------------------
+// ENCODER //
+// rotary encoder pin change interrupt handler
+void readEncoder() {
+  encoder_state = (encoder_state << 4) | (digitalRead(ROTB) << 1) | digitalRead(ROTA);
+  Serial.println(encoder_val);
+  switch (encoder_state) {
+    case 0x23: encoder_val++; break;
+    case 0x32: encoder_val--; break;
+    default: break;
+  }
+}
+void rotate() {
+  unsigned char result = rotary.process();
+  if (result == DIR_CW) {
+    encoder_val++;
+  } else if (result == DIR_CCW) {
+    encoder_val--;
+  }
+}
+// rotary encoder init
+void encoder_init() {
+  // enable pin change interrupts
+  attachInterrupt(digitalPinToInterrupt(ROTA), readEncoder, RISING);
+  attachInterrupt(digitalPinToInterrupt(ROTB), readEncoder, RISING);
+  encoder_state = (digitalRead(ROTB) << 1) | digitalRead(ROTA);
+  interrupts();
+}
+
+// MENU //
+void setupMenu() {
+  // Add menu items to Main menu page
+  menuPageMain.addMenuItem(menuItemLayout);
+  menuPageMain.addMenuItem(menuItemKey);
+  menuPageMain.addMenuItem(menuItemScale);
+  //menuPageMain.addMenuItem(menuItemHighlightScale); REMOVING SOON
+  menuPageMain.addMenuItem(menuItemTranspose);
+  // Add menu items to Layout Select page
+  menuPageLayout.addMenuItem(menuItemWickiHayden);
+  menuPageLayout.addMenuItem(menuItemHarmonicTable);
+  menuPageLayout.addMenuItem(menuItemGerhard);
+  // Specify parent menu page for the Settings menu page
+  menuPageLayout.setParentMenuPage(menuPageMain);
+
+  // Add menu page to menu and set it as current
+  menu.setMenuPageCurrent(menuPageMain);
+}
+
+void wickiHayden() {
+  currentLayout = wickiHaydenLayout;
+  setLayoutLEDs();
+  menu.setMenuPageCurrent(menuPageMain);
+  menu.drawMenu();
+}
+void harmonicTable() {
+  currentLayout = harmonicTableLayout;
+  setLayoutLEDs();
+  menu.setMenuPageCurrent(menuPageMain);
+  menu.drawMenu();
+}
+void gerhard() {
+  currentLayout = gerhardLayout;
+  setLayoutLEDs();
+  menu.setMenuPageCurrent(menuPageMain);
+  menu.drawMenu();
+}
+
+
+// Validation routine of transpose variable
+void validateTranspose() {
+  //Need to add some code here to make sure transpose doesn't get out of hand
+  /*something like
+  if ((transpose + LOWEST NOTE IN ARRAY) < 0) {
+    transpose = 0;
+  } */
+  setLayoutLEDs();
+}
 
-// END OF PROGRAM
-// ------------------------------------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
+// END FUNCTIONS SECTION
\ No newline at end of file