Firmware for HexBoard MIDI controller
-rw-r--r--Buttons.ino77
-rw-r--r--HexBoard_V1.1.ino266
-rw-r--r--HexBoard_V1.ino504
-rw-r--r--LEDs.ino38
-rw-r--r--MIDI.ino15
5 files changed, 396 insertions, 504 deletions
diff --git a/Buttons.ino b/Buttons.ino
new file mode 100644
index 0000000..ecc209f
--- /dev/null
+++ b/Buttons.ino
@@ -0,0 +1,77 @@
+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] % 12) * 5006, 255, 255));
+ noteOn(midiChannel, (currentLayout[i] + octave) % 128, midiVelocity);
+ } else {
+ commandPress(currentLayout[i]);
+ }
+ } else {
+ // If the button is inactive (released)
+ if (currentLayout[i] < 128) {
+ setLayoutLED(i);
+ noteOff(midiChannel, (currentLayout[i] + octave) % 128, 0);
+ } else {
+ commandRelease(currentLayout[i]);
+ }
+ }
+ }
+ }
+}
+
+void heldButtons() {
+ for (int i = 0; i < elementCount; i++) {
+ if (activeButtons[i]) {
+ //if (
+ }
+ }
+} \ No newline at end of file
diff --git a/HexBoard_V1.1.ino b/HexBoard_V1.1.ino
new file mode 100644
index 0000000..6c5b3fd
--- /dev/null
+++ b/HexBoard_V1.1.ino
@@ -0,0 +1,266 @@
+// Hardware Information:
+// Generic RP2040 running at 133MHz with 16MB of flash
+
+// Brilliant resource for dealing with hexagonal coordinates. https://www.redblobgames.com/grids/hexagons/
+// Might be useful for animations and stuff like that.
+
+#include <Arduino.h>
+#include <Adafruit_TinyUSB.h>
+#include <MIDI.h>
+#include <Adafruit_NeoPixel.h>
+
+// 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);
+
+#define LED_PIN 22
+#define LED_COUNT 140
+
+Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_RGB + NEO_KHZ800);
+
+//
+// Button matrix and LED locations
+// Portrait orientation top view:
+// 9 8 7 6 5 4 3 2 1
+// 20 19 18 17 16 15 14 13 12 11
+// 29 28 27 26 25 24 23 22 21
+// 40 39 38 37 36 35 34 33 32 31
+// 49 48 47 46 45 44 43 42 41
+// 60 59 58 57 56 55 54 53 52 51
+// 10 69 68 67 66 65 64 63 62 61
+// 30 80 79 78 77 76 75 74 73 72 71
+// 50 89 88 87 86 85 84 83 82 81
+// 70 100 99 98 97 96 95 94 93 92 91
+// 90 109 108 107 106 105 104 103 102 101
+//110 120 119 118 117 116 115 114 113 112 111
+// 130 129 128 127 126 125 124 123 122 121
+// 140 139 138 137 136 135 134 133 132 131
+
+// 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
+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;
+const int m4p = 2;
+const int m8p = 3;
+// 16 & 17 reserved for lights.
+const byte columnCount = sizeof(columns); // The number of columns in the matrix
+const byte rowCount = 14; // The number of rows in the matrix
+const byte elementCount = columnCount * rowCount; // The number of elements in the matrix
+
+// 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 UNUSED = 255;
+
+#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
+const byte wickiHaydenLayout[elementCount] = {
+ ROW_FLIP(BK_TOG, 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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
+const byte debounceTime = 2; // Global digital button debounce time in milliseconds
+
+// Variables for holding digital button states and activation times
+byte activeButtons[elementCount]; // Array to hold current note button states
+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
+
+
+// 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
+// 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");
+
+ // 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);
+
+ // Set pinModes for the digital button matrix.
+ for (int pinNumber = 0; pinNumber < columnCount; pinNumber++) // For each column pin...
+ {
+ pinMode(columns[pinNumber], INPUT_PULLUP); // set the pinMode to INPUT_PULLUP (+3.3V / HIGH).
+ }
+ pinMode(m1p, OUTPUT); // Setting the row multiplexer pins to output.
+ pinMode(m2p, OUTPUT);
+ pinMode(m4p, OUTPUT);
+ pinMode(m8p, OUTPUT);
+ Serial.begin(115200);
+ strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
+ strip.show(); // Turn OFF all pixels ASAP
+ strip.setBrightness(128); // Set BRIGHTNESS (max = 255)
+ setOctLED();
+ setLayoutLEDs();
+ strip.setPixelColor(layMdSW, 255, 0, 0);
+
+ // wait until device mounted
+ while (!TinyUSBDevice.mounted()) delay(1);
+
+ // Print diagnostic troubleshooting information to serial monitor
+ diagnosticTest();
+}
+
+// ------------------------------------------------------------------------------------------------------------------------------------------------------------
+// START LOOP SECTION
+void loop() {
+ // Store the current time in a uniform variable for this program loop
+ currentTime = millis();
+
+ // Read and store the digital button states of the scanning matrix
+ readDigitalButtons();
+
+ // Act on those buttons
+ playNotes();
+
+ // Held Buttons
+ heldButtons();
+
+ // Do the LEDS
+ strip.show();
+
+ // Read any new MIDI messages
+ MIDI.read();
+}
+// END LOOP SECTION
+// ------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+
+// ------------------------------------------------------------------------------------------------------------------------------------------------------------
+// START FUNCTIONS SECTION
+
+void diagnosticTest() {
+ if (diagnostics > 0) {
+ Serial.println("Zach was here");
+ }
+}
+
+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);
+ }
+ } else if (command == OCT_UP) {
+ if (octave <= 12) {
+ octave += 12;
+ setOctLED();
+ strip.setPixelColor(octUpSW, 120, 120, 120);
+ }
+ } else if (command == LAY_MD) {
+ if (currentLayout == wickiHaydenLayout) {
+ currentLayout = harmonicTableLayout;
+ } else if (currentLayout == harmonicTableLayout) {
+ currentLayout = gerhardLayout;
+ } else {
+ currentLayout = wickiHaydenLayout;
+ }
+ setLayoutLEDs();
+ } else if (command == BK_TOG) {
+ blackKeys = !blackKeys;
+ setLayoutLEDs();
+ }
+}
+void commandRelease(byte command) {
+ if (command == OCT_DN) {
+ setOctLED();
+ } else if (command == OCT_UP) {
+ setOctLED();
+ }
+}
+
+// END FUNCTIONS SECTION
+// ------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+// END OF PROGRAM
+// ------------------------------------------------------------------------------------------------------------------------------------------------------------ \ No newline at end of file
diff --git a/HexBoard_V1.ino b/HexBoard_V1.ino
deleted file mode 100644
index d0a8a84..0000000
--- a/HexBoard_V1.ino
+++ /dev/null
@@ -1,504 +0,0 @@
-// Hardware Information:
-// Teensy LC set to 48MHz with USB type MIDI
-#include <FastLED.h>
-
-#include "Adafruit_BLE.h"
-#include "Adafruit_BluefruitLE_UART.h"
-#include "Adafruit_BLEMIDI.h"
-#include "BluefruitConfig.h"
-
-#define FACTORYRESET_ENABLE 0
-#define MINIMUM_FIRMWARE_VERSION "0.7.0"
-
-#define LEDS_PIN 17
-#define NUM_LEDS 140
-
-Adafruit_BluefruitLE_UART ble(Serial1, -1);
-
-Adafruit_BLEMIDI blemidi(ble);
-
-bool bleModuleEnabled = false;
-
-bool isConnected = false;
-
-// Bluetooth error messages
-void error(const __FlashStringHelper*err) {
- Serial.println(err);
- while (1);
-}
-//Bluetooth callbacks
-void connected(void)
-{
- isConnected = true;
- Serial.println("CONNECTED!");
- //Zach make bluetooth light go solid once connected
-}
-void disconnected(void)
-{
- Serial.println("disconnected");
- isConnected = false;
- //Zach make bluetooth light start blinking again once disconnected
-}
-
-CRGB leds[NUM_LEDS];
-
-void init_leds()
-{
- FastLED.addLeds<WS2811, LEDS_PIN, RGB>(leds, NUM_LEDS);
- /*Max Brightness. Recommended 100 or lower for usb power to stay under 2 amps. Increase up to 255 at your own risk.
- Board can handle full power (theoretically 8.4 amps at max brightness) while only getting warm, but external power
- is required to meet this. Use capable power supply on EXT PWR headers.*/
- FastLED.setBrightness(200);
- FastLED.setMaxPowerInVoltsAndMilliamps(5,1000);
- for (int i=0; i < NUM_LEDS; i++) {
- leds[i] = CRGB::Black;
- }
-}
-
-bool bluetooth = false;
-void init_bluetooth()
-{
-/* Initialise the module */
- Serial.print(F("Initialising the Bluefruit LE module: "));
-
- if ( !ble.begin(VERBOSE_MODE) )
- {
- error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?"));
- }
- Serial.println( F("OK!") );
-
- if ( FACTORYRESET_ENABLE )
- {
- /* Perform a factory reset to make sure everything is in a known state */
- Serial.println(F("Performing a factory reset: "));
- if ( ! ble.factoryReset() ) {
- error(F("Couldn't factory reset"));
- }
- }
-
- //ble.sendCommandCheckOK(F("AT+uartflow=off"));
- ble.echo(false);
-
- Serial.println("Requesting Bluefruit info:");
- /* Print Bluefruit information */
- ble.info();
-
- /* Set BLE callbacks */
- ble.setConnectCallback(connected);
- ble.setDisconnectCallback(disconnected);
-
- Serial.println(F("Enable MIDI: "));
- if ( ! blemidi.begin(true) )
- {
- error(F("Could not enable Bluetooth MIDI"));
- }
-
- ble.verbose(false);
- Serial.print(F("Waiting for a connection..."));
-
- bluetooth = true;
- //Zach add bluetooth button blinking here until connection made
-}
-void shutdown_bluetooth()
-{
- ble.end();
- bluetooth = false;
-}
-
-//
-// Button matrix and LED locations
-// Portrait orientation top view:
-// 9 8 7 6 5 4 3 2 1
-// 20 19 18 17 16 15 14 13 12 11
-// 29 28 27 26 25 24 23 22 21
-// 40 39 38 37 36 35 34 33 32 31
-// 49 48 47 46 45 44 43 42 41
-// 60 59 58 57 56 55 54 53 52 51
-// 10 69 68 67 66 65 64 63 62 61
-// 30 80 79 78 77 76 75 74 73 72 71
-// 50 89 88 87 86 85 84 83 82 81
-// 70 100 99 98 97 96 95 94 93 92 91
-// 90 109 108 107 106 105 104 103 102 101
-//110 120 119 118 117 116 115 114 113 112 111
-// 130 129 128 127 126 125 124 123 122 121
-// 140 139 138 137 136 135 134 133 132 131
-
-//DIAGNOSTICS
-int diagnostics = 0;
-
-// Define digital button matrix pins
-const byte columns[] = { 25, 24, 9, 8, 7, 6, 5, 4, 3, 2}; // Column pins in order from right to left
-const byte rows[] = {10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 26}; // Row pins in order from top to bottom
-// 16 & 17 reserved for lights.
-const byte columnCount = sizeof(columns); // The number of columns in the matrix
-const byte rowCount = sizeof(rows); // The number of rows in the matrix
-const byte elementCount = columnCount * rowCount;// The number of elements in the matrix
-
-// 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 UNUSED = 255;
-
-#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 we typed them in wrong???
-//#define NO_FLIP(x, ix, viii, vii, vi, v, iv, iii, ii, i) i, x, ix, viii, vii, vi, v, iv, iii, ii
-
-
-// MIDI note value tables
-const byte wickiHaydenLayout[elementCount] = {
-ROW_FLIP(BK_TOG, 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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( 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 timeBothPressed;
-const byte debounceTime = 2; // Global digital button debounce time in milliseconds
-
-// Variables for holding digital button states and activation times
-byte activeButtons[elementCount]; // Array to hold current note button states
-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
-
-
-// MIDI channel assignment
-byte midiChannel = 0; // 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 velocity = 95; // Default velocity
-// END SETUP SECTION
-// ------------------------------------------------------------------------------------------------------------------------------------------------------------
-
-void setup()
-{
- // Set pinModes for the digital button matrix.
- for (int pinNumber = 0; pinNumber < columnCount; pinNumber++) // For each column pin...
- {
- pinMode(columns[pinNumber], INPUT_PULLUP); // set the pinMode to INPUT_PULLUP (+3.3V / HIGH).
- }
- for (int pinNumber = 0; pinNumber < rowCount; pinNumber++) // For each row pin...
- {
- pinMode(rows[pinNumber], INPUT); // Set the pinMode to INPUT (0V / LOW).
- }
- Serial.begin(115200);
- if (bleModuleEnabled == true){
- init_bluetooth();
- }
- init_leds();
- setOctLED();
- setLayoutLEDs();
- leds[layMdSW] = CRGB::Red;
-
- // Print diagnostic troubleshooting information to serial monitor
- diagnosticTest();
-}
-
-// ------------------------------------------------------------------------------------------------------------------------------------------------------------
-// START LOOP SECTION
-void loop()
-{
- // Store the current time in a uniform variable for this program loop
- currentTime = millis();
-
- // Read and store the digital button states of the scanning matrix
- readDigitalButtons();
-
- // Act on those buttons
- playNotes();
-
- // Held Buttons
- heldButtons();
-
- // Do the LEDS
- FastLED.show();
-}
-// END LOOP SECTION
-// ------------------------------------------------------------------------------------------------------------------------------------------------------------
-
-
-// ------------------------------------------------------------------------------------------------------------------------------------------------------------
-// START FUNCTIONS SECTION
-
-void diagnosticTest()
-{
- if (diagnostics > 0) {
- Serial.println("Zach was here");
- }
-}
-
-void readDigitalButtons()
-{
- if (diagnostics == 1){
- Serial.println();
- }
- // Button Deck
- for (byte rowIndex = 0; rowIndex < rowCount; rowIndex++) // Iterate through each of the row pins.
- {
- byte rowPin = rows[rowIndex]; // Hold the currently selected row pin in a variable.
- pinMode(rowPin, OUTPUT); // Set that row pin to OUTPUT mode and...
- digitalWrite(rowPin, LOW); // set the pin state to LOW turning it into a temporary ground.
- 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(50); // 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);
- }
- // Set the selected row pin back to INPUT mode (0V / LOW).
- pinMode(rowPin, 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) {
- //leds[i] = CRGB::White;
- leds[i] = CHSV((currentLayout[i] % 12) * 21, 255, 255);
- noteOn(midiChannel, (currentLayout[i] + octave) % 128 , velocity);
- } else {
- commandPress(currentLayout[i]);
- }
- } else {
- // If the button is inactive (released)
- if (currentLayout[i] < 128) {
- setLayoutLED(i);
- //leds[i] ;
- noteOff(midiChannel, (currentLayout[i] + octave) % 128, 0);
- } else {
- commandRelease(currentLayout[i]);
- }
- }
- }
- }
-}
-
-void heldButtons()
-{
- for (int i = 0; i < elementCount; i++) {
- if (activeButtons[i]) {
- //if (
- }
- }
-}
-
-// MIDI PACKET FUNCTIONS
-
-// Send MIDI Note On
-void noteOn(byte channel, byte pitch, byte velocity)
-{
- if (isConnected == true){
- blemidi.send(0x90, pitch, velocity);
- }
- else {
- usbMIDI.sendNoteOn(pitch, velocity, channel);
- }
-}
-// Send MIDI Note Off
-void noteOff(byte channel, byte pitch, byte velocity)
-{
- if (isConnected == true){
- blemidi.send(0x80, pitch, velocity);
- }
- else {
- usbMIDI.sendNoteOff(pitch, velocity, channel);
- }
-}
-
-void commandPress(byte command)
-{
- // Keep octave between ~-12~ 0 and 24
- if(command == OCT_DN) {
- if (octave >= 12) {
- octave -= 12;
- setOctLED(); leds[octDnSW] = CRGB::White;
- }
- } else if (command == OCT_UP) {
- if (octave <= 12) {
- octave += 12;
- setOctLED(); leds[octUpSW] = CRGB::White;
- }
- } else if (command == LAY_MD) {
- if (currentLayout == wickiHaydenLayout) {
- currentLayout = harmonicTableLayout;
- } else if (currentLayout == harmonicTableLayout) {
- currentLayout = gerhardLayout;
- } else {
- currentLayout = wickiHaydenLayout;
- }
- 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 setOctLED()
-{
- /*if (octave <= -12) {
- leds[octUpSW].setRGB(0xA0, 0, 0x20);
- leds[octDnSW] = CRGB::Black; // No lower to go.
- } else */if (octave <= 0) {
- leds[octUpSW] = CRGB::Purple;
- leds[octDnSW] = CRGB::Black; // No lower to go.
- } else if (octave <= 12) {
- leds[octUpSW] = CRGB::Blue;
- leds[octDnSW].setRGB(0xA0, 0, 0x20);
- } else if (octave <= 24) {
- leds[octUpSW] = CRGB::Black; // No higher to go.
- leds[octDnSW] = CRGB::Purple;
- }
-}
-
-void setLayoutLEDs()
-{
- for (int i = 0; i < elementCount; i++) {
- if (currentLayout[i] <= 127) {
- setLayoutLED(i);
- }
- }
-}
-void setLayoutLED(int i) {
- leds[i] = CHSV((currentLayout[i] % 12) * 21, 255, 120);
- // black keys darker
- if(blackKeys){
- // LEET programmers stuff
- switch(currentLayout[i] % 12) {
- // If it is one of the black keys, fall through to case 10.
- case 1:
- case 3:
- case 6:
- case 8:
- // bitshift by 2 (efficient division by four)
- case 10: leds[i] >>= 2; break;
- // otherwise it was a white key. Do nothing
- default: break;
- }
- }
-}
-
-// Control Change
-// 1st byte = Event type (0x0B = Control Change).
-// 2nd byte = Event type bitwise ORed with MIDI channel.
-// 3rd byte = MIDI CC number (7-bit range 0-127).
-// 4th byte = Control value (7-bit range 0-127).
-void controlChange(byte channel, byte control, byte value)
-{
-
-}
-
-// Pitch Bend
-// (14 bit value 0-16363, neutral position = 8192)
-// 1st byte = Event type (0x0E = Pitch bend change).
-// 2nd byte = Event type bitwise ORed with MIDI channel.
-// 3rd byte = The 7 least significant bits of the value.
-// 4th byte = The 7 most significant bits of the value.
-void pitchBendChange(byte channel, byte lowValue, byte highValue)
-{
-
-}
-
-// END FUNCTIONS SECTION
-// ------------------------------------------------------------------------------------------------------------------------------------------------------------
-
-// END OF PROGRAM
-// ------------------------------------------------------------------------------------------------------------------------------------------------------------
diff --git a/LEDs.ino b/LEDs.ino
new file mode 100644
index 0000000..5b72b65
--- /dev/null
+++ b/LEDs.ino
@@ -0,0 +1,38 @@
+void setOctLED() {
+ if (octave <= 0) {
+ strip.setPixelColor(octUpSW, 120, 0, 120);
+ strip.setPixelColor(octDnSW, 0, 0, 0); // No lower to go.
+ } else if (octave <= 12) {
+ strip.setPixelColor(octUpSW, 0, 0, 120);
+ strip.setPixelColor(octDnSW, 120, 0, 0);
+ } else if (octave <= 24) {
+ strip.setPixelColor(octUpSW, 0, 0, 0); //No higher to go.
+ strip.setPixelColor(octDnSW, 120, 0, 120);
+ }
+}
+
+void setLayoutLEDs() {
+ for (int i = 0; i < elementCount; i++) {
+ if (currentLayout[i] <= 127) {
+ setLayoutLED(i);
+ }
+ }
+}
+void setLayoutLED(int i) {
+ strip.setPixelColor(i, strip.ColorHSV((currentLayout[i] % 12) * 5006, 255, 120));
+ // black keys darker
+ if (blackKeys) {
+ // LEET programmers stuff
+ switch (currentLayout[i] % 12) {
+ // If it is one of the black keys, fall through to case 10.
+ case 1:
+ case 3:
+ case 6:
+ case 8:
+ // bitshift by 2 (efficient division by four)
+ case 10: strip.setPixelColor(i, strip.ColorHSV((currentLayout[i] % 12) * 5006, 255, 30)); break;
+ // otherwise it was a white key. Do nothing
+ default: break;
+ }
+ }
+} \ No newline at end of file
diff --git a/MIDI.ino b/MIDI.ino
new file mode 100644
index 0000000..77c786b
--- /dev/null
+++ b/MIDI.ino
@@ -0,0 +1,15 @@
+// Send MIDI 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 MIDI Note Off
+void noteOff(byte channel, byte pitch, byte velocity) {
+ MIDI.sendNoteOff(pitch, velocity, channel);
+} \ No newline at end of file