#define DEBUG
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <light_CD74HC4067.h>
#include <INHK_Pot3.h>
#include "Adafruit_NeoTrellis.h"
#include <ShiftRegister74HC595.h>
#include <Encoder.h>
#define POTS_MIDI_CH 3 // midi channel to be used with POTENTIOMETERS and ENCODERS
#define LED_BUTTON_MIDI_CH 4 // midi channel to be used with BUTTONS and LEDS
#define MIDI_MSG_OFFSET 14 // global offset for midi messages for all midi message (avoid first 14 numbers)
#define EXTRA_POT_MIDI_MSG_OFFSET 60 // global offset for midi CC for EXTRA POTS
#define LEFT_ENC_MIDI_MSG_OFFSET 70 // global offset for midi CC for left encoder
#define RIGHT_ENC_MIDI_MSG_OFFSET 80 // global offset for midi CC for right encoder
#define EXTRA_POT_NUM 3 // number of additional potentiometers (direct to board, not to mux)
// LCD OBJECTS////////////////////////////////////////////////////////////////////////////////////////////////////////////////
LiquidCrystal_I2C lcdLeft(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcdRight(0x26,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
// LED GLOBALS ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define NUM_LED 16
ShiftRegister74HC595<2> sr(4, 2, 3); // shift register object, par: <number of shift registers> (data pin, clock pin, latch pin)
uint8_t extraLedR_pin= 22; // additional led not wired to shift registers
uint8_t extraLedL_pin = 23; // additional led not wired to shift registers
// POTS GLOBALS///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// mux pots:
CD74HC4067 mux(17, 16, 15, 14); // create a new CD74HC4067 object with its four select lines
const uint8_t signal_pin0 = A2; // Pin connected to Sig pin of CD74HC4067
const uint8_t signal_pin1 = A0; // Pin connected to Sig pin of CD74HC4067
int lastValues[32]; // Store the last values for each of the 32 potentiometers (16 per multiplexer)
const uint8_t changeThreshold = 2; // General threshold to avoid jitter
const uint8_t edgeThreshold = 3; // Stricter threshold near the edges (0 or 127)
// extra pots:
Potentiometer Pot[EXTRA_POT_NUM] = {(A8), (A9), (A10)}; // Creates a number (EXTRA_POT_NUM) of objects of Potentiometer class and passes the pin number as argument
int lastextraPotReading[EXTRA_POT_NUM]; // Variable to store the last readings for comparison
// BUTTON MULTIPLEXER GLOBAL///////////////////////////////////////////////////////////////////////////////////////////////////
const uint8_t muxPin1 = 18; // Signal pin of the first multiplexer
const uint8_t muxPin2 = 20; // Signal pin of the second multiplexer
const uint8_t s0 = 17; // Select pins
const uint8_t s1 = 16;
const uint8_t s2 = 15;
const uint8_t s3 = 14;
const uint8_t numChannels = 16; // Number of channels per multiplexer
uint8_t buttonPState[2 * numChannels]; // Last stable state of the button
uint8_t buttonCState[2 * numChannels]; // button current state
unsigned long lastTimeButtonStateChanged[2 * numChannels] = {0}; // last time button state changed
unsigned long debounceDuration = 20; // debounce time, increase if output flickers
// ENCODER GLOBALS //////////////////////////////////////////////////////////////
Encoder knobLeft(7, 8);
Encoder knobRight(10, 9);
long positionLeft = -999;
long positionRight = -999;
uint8_t leftEncModeOffset = 0;
uint8_t rightEncModeOffset = 0;
// LCD GLOBALS/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum lcdActiveMode{ // LCD display modes
normal,
effects,
scratch
}leftLCD_activeMode, rightLCD_activeMode;
// TRELLIS GLOBALS////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define TRELLIS1_MIDI_CH 1
#define TRELLIS2_MIDI_CH 2
Adafruit_NeoTrellis trellis1; // Instantiate NeoTrellis board - left trellis
Adafruit_NeoTrellis trellis2; // Instantiate NeoTrellis board - right trellis
unsigned long prevMillisTrellis1 = 0; // Store the last time the first trellis was read
unsigned long prevMillisTrellis2 = 0; // Store the last time the second trellis was read
#define INTERVAL 20 // Interval to read the trellises (20 milliseconds)
uint8_t leftDeck = 0;
uint8_t leftPad; // doesn't need to be initialized as it responds to switch position (button 14)
uint8_t offset_Trellis1 = 0; // offset for the trellis modes ie. DECK A = 0, PAD A 16, DECK C = 32, PAD C 48
uint8_t rightDeck = 0;
uint8_t rightPad; // doesn't need to be initialized as it responds to switch position (button 23)
uint8_t offset_Trellis2 = 0; // offset for the trellis modes ie. DECK B = 0, DECK D = 16, PAD B 32, PAD B 48
enum TrellisMode {
deck0,
pad0,
deck1,
pad1
};
TrellisMode trellis1_activeMode;
TrellisMode trellis2_activeMode;
// Define a callback for key presses on the first board
TrellisCallback press1(keyEvent evt){
if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) {
usbMIDI.sendNoteOn(evt.bit.NUM + MIDI_MSG_OFFSET + offset_Trellis1, 127, TRELLIS1_MIDI_CH);
// trellis1.pixels.setPixelColor(evt.bit.NUM, 255, 0, 0); // set the keypad colour when button is pressed
#ifdef DEBUG
Serial.print(F("MIDI MSG: Note On\t")); Serial.print(evt.bit.NUM + MIDI_MSG_OFFSET + offset_Trellis1);
Serial.print(F("\t CH"));Serial.println(TRELLIS1_MIDI_CH);
#endif
} else if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING) {
// trellis1.pixels.setPixelColor(evt.bit.NUM, 0); // set the keypad colour when button is released
usbMIDI.sendNoteOff(evt.bit.NUM + MIDI_MSG_OFFSET + offset_Trellis1, 127, TRELLIS1_MIDI_CH);
#ifdef DEBUG
Serial.print(F("MIDI MSG: Note Off\t")); Serial.print(evt.bit.NUM + MIDI_MSG_OFFSET + offset_Trellis1);
Serial.print(F("\t CH"));Serial.println(TRELLIS1_MIDI_CH);
#endif
}
trellis1.pixels.show();
return 0;
}
// Define a callback for key presses on the second board
TrellisCallback press2(keyEvent evt){
if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) {
usbMIDI.sendNoteOn(evt.bit.NUM + MIDI_MSG_OFFSET + offset_Trellis2, 127, TRELLIS2_MIDI_CH);
// trellis2.pixels.setPixelColor(evt.bit.NUM, 0, 255, 0); // set the keypad colour when button is pressed
#ifdef DEBUG
Serial.print(F("MIDI MSG: Note On\t")); Serial.print(evt.bit.NUM + MIDI_MSG_OFFSET + offset_Trellis2);
Serial.print(F("\t CH"));Serial.println(TRELLIS2_MIDI_CH);
#endif
} else if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING) {
// trellis2.pixels.setPixelColor(evt.bit.NUM, 0); // set the keypad colour when button is released
usbMIDI.sendNoteOff(evt.bit.NUM + MIDI_MSG_OFFSET + offset_Trellis2, 127, TRELLIS2_MIDI_CH);
#ifdef DEBUG
Serial.print(F("MIDI MSG: Note Off\t")); Serial.print(evt.bit.NUM + MIDI_MSG_OFFSET + offset_Trellis2);
Serial.print(F("\t CH"));Serial.println(TRELLIS2_MIDI_CH);
#endif
}
trellis2.pixels.show();
return 0;
}
//SETUP AND LOOP /////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600);
initializeLEDS();
initializeButtons();
initializePots();
initializeExtraPots();
initilizeLCD();
initializeTrellis();
usbMIDI.setHandleNoteOff(OnNoteOff);
usbMIDI.setHandleNoteOn(OnNoteOn);
trellisModesSelection(); //updates the trellis selection modes
leftLCDmodeSpy(); // refresh mode LED
rightLCDmodeSpy();
#ifdef DEBUG
Serial.println();
Serial.println(F("SETUP COMPLETED"));
Serial.println();
#endif
}
void loop() {
readEncoders(); // Call the function that reads encoders
updatePots(); // Call the function that reads potentiometers attached to mux
updateExtraPots(); // Call the function that reads extra potentiometers (using pot3 library with smooth output)
readButtons(); // Call the function that reads buttons and performs debugging
readBothTrellis(); // Call the function that reads both trellis
usbMIDI.read();
}
//LCD FUNCTIONS/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void initilizeLCD(){
lcdLeft.init(); // initialize the lcd
lcdLeft.init();
lcdLeft.backlight();
lcdLeft.setCursor(4,0); // Print a message to the LCD.
lcdLeft.print("INHK DJ");
lcdLeft.setCursor(1,1);
lcdLeft.print("By A.Monderisi");
lcdRight.init(); // initialize the lcd
lcdRight.init();
lcdRight.backlight();
lcdRight.setCursor(4,0); // Print a message to the LCD.
lcdRight.print("INHK DJ");
lcdRight.setCursor(0,1);
lcdRight.print("...I am so cool!");
}
//LED FUNCTIONS/////////////////////////////////////////////////////////////////////////////////////////////////
void initializeLEDS(){
pinMode(extraLedR_pin, OUTPUT);
pinMode(extraLedL_pin, OUTPUT);
ledBlink();
extraLedBlink();
extraLedBlink();
#ifdef DEBUG
Serial.println();
Serial.println(F("LEDs initialization completed!"));
Serial.println();
#endif
}
void ledBlink(){
for (int i = 0; i < NUM_LED; i++) {
sr.set(i, LOW); // set single pin HIGH
}
delay(250);
for (int i = 0; i < NUM_LED; i++) {
sr.set(i, HIGH); // set single pin HIGH
}
delay(250);
for (int i = 0; i < NUM_LED; i++) {
sr.set(i, LOW); // set single pin HIGH
}
delay(250);
for (int i = 0; i < NUM_LED; i++) {
sr.set(i, HIGH); // set single pin HIGH
}
delay(250);
for (int i = 0; i < NUM_LED; i++) {
sr.set(i, LOW); // set single pin HIGH
}
delay(250);
}
void extraLedBlink(){
digitalWrite(extraLedR_pin, HIGH); // sets the digital pin 13 on
digitalWrite(extraLedL_pin, HIGH); // sets the digital pin 13 ondelay(500); // waits for a second
delay(250);
digitalWrite(extraLedR_pin, LOW); // sets the digital pin 13 off
digitalWrite(extraLedL_pin, LOW); // sets the digital pin 13 offdelay(500); // waits for a second
delay(250); // waits for a second
}
//BUTTONS FUNCTIONS///////////////////////////////////////////////////////////////////////////////////////////////////////////
void initializeButtons() {
#ifdef DEBUG
Serial.println();
Serial.println(F("Starting BUTTONS initialization..."));
Serial.println();
#endif
// Set multiplexer control pins as outputs
pinMode(s0, OUTPUT);
pinMode(s1, OUTPUT);
pinMode(s2, OUTPUT);
pinMode(s3, OUTPUT);
// Set the multiplexer signal pins as input with internal pull-up resistor
pinMode(muxPin1, INPUT_PULLUP);
pinMode(muxPin2, INPUT_PULLUP);
buttonsFirstReading();
#ifdef DEBUG
Serial.println();
Serial.println(F("BUTTONS initialization completed!"));
Serial.println();
#endif
}
// Function to read from the correct multiplexer based on index (0-31)
int readMux(int index) {
int controlPin[] = {s0, s1, s2, s3};
int channel = index % numChannels;
int muxPin = (index < numChannels) ? muxPin1 : muxPin2;
// Set the control pins to select the correct channel
for (int i = 0; i < 4; i++) {
digitalWrite(controlPin[i], (channel & (1 << i)) ? HIGH : LOW);
}
// Read the digital value from the corresponding multiplexer signal pin
return digitalRead(muxPin);
}
void readButtons() {
for (int i = 0; i < 2 * numChannels; i++) { // Read all 32 channels (0-15 for muxPin1, 16-31 for muxPin2)
buttonCState[i] = readMux(i);
if((millis() - lastTimeButtonStateChanged[i]) >debounceDuration){
if (buttonCState[i] != buttonPState[i]) { // Check if the button state has changed
lastTimeButtonStateChanged[i] = millis();
(buttonCState[i] == LOW) ? button_Pressed_Outcome(i) : button_Released_Outcome(i);
buttonPState[i] = buttonCState[i]; // Update the last known button state
}
}
}
}
void button_Pressed_Outcome(uint8_t index){
#ifdef DEBUG
Serial.print(F("button pressed: "));
Serial.println(index);
Serial.print("\t");
#endif
switch (index){
case 3:
leftLCD_activeMode = normal;
#ifdef DEBUG
Serial.println(F("***BUTTON NOT YET ASSIGNED***"));
#endif
break;
case 4:
leftLCD_activeMode = effects;
#ifdef DEBUG
Serial.println(F("***BUTTON NOT YET ASSIGNED***"));
#endif
break;
case 10:
leftLCD_activeMode = scratch;
#ifdef DEBUG
Serial.println(F("***BUTTON NOT YET ASSIGNED***"));
#endif
break;
case 21:
rightLCD_activeMode = normal;
#ifdef DEBUG
Serial.println(F("***BUTTON NOT YET ASSIGNED***"));
#endif
break;
case 31:
rightLCD_activeMode = effects;
#ifdef DEBUG
Serial.println(F("***BUTTON NOT YET ASSIGNED***"));
#endif
break;
case 30:
rightLCD_activeMode = scratch;
#ifdef DEBUG
Serial.println(F("***BUTTON NOT YET ASSIGNED***"));
#endif
break;
case 14:
leftPad = 1; //change left PAD status to active
trellisModesSelection(); //updates the trellis selection modes
leftLCDmodeSpy(); //update spy LED and print debug message
trellis1.pixels.clear(); // clear Trellis LEDs
break;
case 12:
leftDeck = !leftDeck; //invert active left DECK from A to C and viceversa
trellisModesSelection(); //updates the trellis selection modes
leftLCDmodeSpy(); //update spy LED and print debug message
trellis1.pixels.clear(); // clear Trellis LEDs
break;
case 23:
rightPad = 0; //change right PAD status to active (right and left pad are wired the opposite way, here 0 is active)
trellisModesSelection(); //updates the trellis selection modes
rightLCDmodeSpy(); //update spy LED and print debug message
trellis2.pixels.clear(); // clear Trellis LEDs
break;
case 19:
rightDeck = !rightDeck; //invert active right DECK from B to D and viceversa
trellisModesSelection(); //updates the trellis selection modes
rightLCDmodeSpy(); //update spy LED and print debug messageZ
trellis2.pixels.clear(); // clear Trellis LEDs
break;
#ifdef DEBUG
case 11: Serial.println(F("left ENC button pressed")); break;
case 18: Serial.println(F("right ENC button pressed")); break;
#endif
default:
usbMIDI.sendNoteOn(MIDI_MSG_OFFSET + index, 127, LED_BUTTON_MIDI_CH);
#ifdef DEBUG
Serial.print(F("MIDI msg -> Note ON: "));
Serial.print(MIDI_MSG_OFFSET + index);
Serial.println(F("\t value: 127"));
#endif
break;
}
}
void button_Released_Outcome(uint8_t index){
#ifdef DEBUG
Serial.print(F("button released: "));
Serial.print(index);
Serial.print("\t");
#endif
switch (index){
case 3:
#ifdef DEBUG
Serial.println(F("BUTTON NOT YET ASSIGNED"));
#endif
break;
case 4:
#ifdef DEBUG
Serial.println(F("BUTTON NOT YET ASSIGNED"));
#endif
break;
case 10:
#ifdef DEBUG
Serial.println(F("BUTTON NOT YET ASSIGNED"));
#endif
break;
case 21:
#ifdef DEBUG
Serial.println(F("BUTTON NOT YET ASSIGNED"));
#endif
break;
case 31:
#ifdef DEBUG
Serial.println(F("BUTTON NOT YET ASSIGNED"));
#endif
break;
case 30:
#ifdef DEBUG
Serial.println(F("BUTTON NOT YET ASSIGNED"));
#endif
break;
case 14:
leftPad = 0; //change left PAD status to inactive
trellisModesSelection(); //updates the trellis selection modes
leftLCDmodeSpy(); //update spy LED and print debug message
trellis1.pixels.clear(); // clear Trellis LEDs
break;
case 12:
#ifdef DEBUG
Serial.println(F("Button for A/C selection"));
#endif
break;
case 23:
rightPad = 1; //change right PAD status to inactive
trellisModesSelection(); //updates the trellis selection modes
rightLCDmodeSpy(); //update spy LED and print debug message
trellis2.pixels.clear(); // clear Trellis LEDs
break;
case 19:
#ifdef DEBUG
Serial.println(F("Button for B/D selection"));
#endif
break;
#ifdef DEBUG
case 11: Serial.println(F("left ENC button released")); break;
case 18: Serial.println(F("right ENC button released")); break;
#endif
default:
usbMIDI.sendNoteOff(MIDI_MSG_OFFSET + index, 127, LED_BUTTON_MIDI_CH);
#ifdef DEBUG
Serial.print(F("MIDI-> Note OFF: "));
Serial.print(MIDI_MSG_OFFSET + index);
Serial.println(F("\t value: 127"));
#endif
break;
}
}
void buttonsFirstReading() {
for (int i = 0; i < 2 * numChannels; i++) { // Read all 32 channels (0-15 for muxPin1, 16-31 for muxPin2)
buttonCState[i] = readMux(i);
#ifdef DEBUG
Serial.print(F("Button n: ")); Serial.print(i);
Serial.print(F("\t Value: ")); Serial.println(buttonCState[i]);
#endif
}
}
//POTS FUNTIONS///////////////////////////////////////////////////////////////////////////////////////////////////////////////
void initializePots(){
pinMode(signal_pin0, INPUT); // Set as input for reading through signal pin
pinMode(signal_pin1, INPUT); // Set as input for reading through signal pin
// Initialize lastValues to a value that will always trigger a change
for (int i = 0; i < 32; i++) {
lastValues[i] = -1; // Use -1 as an initial value to ensure the first read triggers a MIDI message
}
#ifdef DEBUG
Serial.println();
Serial.println(F("MUX POTS initialization completed!"));
Serial.println();
#endif
}
void updatePots(){
readMux(0, signal_pin0); // Read from the first multiplexer
readMux(1, signal_pin1); // Read from the second multiplexer
}
void readMux(uint16_t num, uint8_t pin) {
// Loop through channels 0 - 15
for (int i = 0; i < 16; i++) {
mux.channel(i); // Select the channel on the multiplexer
int val = (analogRead(pin) >> 3); // Read analog value and scale it down (0-127 for MIDI)
int index = i + num * 16; // Calculate the index in the lastValues array
// Force values close to 0 or 127 to be exactly 0 or 127
if (val ==1) val = 0; // Snap to 0 if the value is 1
// Handle jitter at edges (near 0 or 127) with stricter threshold
if ((val <= edgeThreshold && abs(val - lastValues[index]) >= edgeThreshold) ||
(val >= 127 - edgeThreshold && abs(val - lastValues[index]) >= edgeThreshold) ||
// For general case, use the standard threshold
abs(val - lastValues[index]) >= changeThreshold) {
usbMIDI.sendControlChange((MIDI_MSG_OFFSET + i + num * 16), val, POTS_MIDI_CH); // Send MIDI message
lastValues[index] = val; // Update the stored value
#ifdef DEBUG
// Print only when the value changes significantly, showing mux, channel, and value
Serial.print(F("Mux-"));
Serial.print(num); // Print which mux (0 or 1) this value is from
Serial.print(F("\t| Ch-"));
Serial.print(i); // Print which channel in the mux
Serial.print(F("\t| Val="));
Serial.print(val); // Print the new value
Serial.print(F("\t| MIDI MSG= CH:"));
Serial.print(POTS_MIDI_CH); // Print the midi channel
Serial.print(F("\t| cc:"));
Serial.print(MIDI_MSG_OFFSET + i + num * 16); // Print the midi cc
Serial.print(F("\t| value:"));
Serial.println(val);
#endif
}
}
}
//EXTRAPOTS FUNTIONS//////////////////////////////////////////////////////////////////////////////////////////////////////////
void initializeExtraPots(){
// Initialize last readings
for (int i = 0; i < EXTRA_POT_NUM; i++) {
lastextraPotReading[i] = Pot[i].readValue();
}
#ifdef DEBUG
Serial.print(F("Number of EXTRA POTS found: "));
Serial.println(Potentiometer::getCounter());
Serial.println(F("EXTRA POTS initialization completed!"));
Serial.println();
#endif
}
void updateExtraPots() { // Update and print potentiometer readings if they have changed
for (int i = 0; i < EXTRA_POT_NUM; i++) {
int val = Pot[i].readValue();
if (val != lastextraPotReading[i]) { // Check if the current reading is different from the last reading
usbMIDI.sendControlChange((EXTRA_POT_MIDI_MSG_OFFSET + i), val, POTS_MIDI_CH); // Send M
lastextraPotReading[i] = val; // Update last reading
#ifdef DEBUG
Serial.print(F("Pot #")); Serial.print(i + 1);
Serial.write('\t');
Serial.print(val);
Serial.write('\t');
Serial.print(F("cc:"));
Serial.print(EXTRA_POT_MIDI_MSG_OFFSET + i);
Serial.write('\t');
Serial.print(F("CH:"));
Serial.println(POTS_MIDI_CH);
#endif
}
}
}
//ENCODERS FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////
void readEncoders(){
long newLeft;
newLeft = knobLeft.read();
if (newLeft != positionLeft) {
uint8_t val;
val = (newLeft > positionLeft) ? 127 : 1;
usbMIDI.sendControlChange((LEFT_ENC_MIDI_MSG_OFFSET + leftEncModeOffset), val, POTS_MIDI_CH);
#ifdef DEBUG
Serial.println();
Serial.print(F("Left ENC position = "));
Serial.println(newLeft);
#endif
positionLeft = newLeft;
}
long newRight;
newRight = knobRight.read();
if(newRight != positionRight){
uint8_t val;
val = (newRight > positionRight) ? 127 : 1;
usbMIDI.sendControlChange((RIGHT_ENC_MIDI_MSG_OFFSET + rightEncModeOffset), val, POTS_MIDI_CH);
#ifdef DEBUG
Serial.print(F("Right ENC position = "));
Serial.println(newRight);
Serial.println();
#endif
positionRight = newRight;
}
}
//TRELLIS FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////
void initializeTrellis(){
// Initialize the first NeoTrellis board
if (!trellis1.begin(0x2E)) { // Address for the first board
#ifdef DEBUG
Serial.println();
Serial.println(F("Could not start trellis 1"));
#endif
while(1) delay(1);
} else {
#ifdef DEBUG
Serial.println();
Serial.println(F("Trellis 1 started"));
#endif
}
// Initialize the second NeoTrellis board
if (!trellis2.begin(0x2F)) { // Address for the second board
#ifdef DEBUG
Serial.println();
Serial.println(F("Could not start trellis 2"));
#endif
while(1) delay(1);
} else {
#ifdef DEBUG
Serial.println();
Serial.println(F("Trellis 2 started"));
Serial.println();
#endif
}
// Activate all keys and set callbacks for the first and second board
for(int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++){
trellis1.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
trellis1.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
trellis1.registerCallback(i, press1);
}
for(int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++){
trellis2.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
trellis2.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
trellis2.registerCallback(i, press2);
}
// Do a little animation to show we're on for the first and second board
for (uint16_t i = 0; i < trellis1.pixels.numPixels(); i++) {
trellis1.pixels.setPixelColor(i, Wheel(map(i, 0, trellis1.pixels.numPixels(), 0, 255)));
trellis1.pixels.show();
delay(25);
}
for (uint16_t i = 0; i < trellis1.pixels.numPixels(); i++) {
trellis1.pixels.setPixelColor(i, 0x000000);
trellis1.pixels.show();
delay(25);
}
for (uint16_t i = 0; i < trellis2.pixels.numPixels(); i++) {
trellis2.pixels.setPixelColor(i, Wheel(map(i, 0, trellis2.pixels.numPixels(), 0, 255)));
trellis2.pixels.show();
delay(25);
}
for (uint16_t i = 0; i < trellis2.pixels.numPixels(); i++) {
trellis2.pixels.setPixelColor(i, 0x000000);
trellis2.pixels.show();
delay(25);
}
}
void readBothTrellis(){
unsigned long currentMillis = millis(); // Get the current time
if (currentMillis - prevMillisTrellis1 >= INTERVAL) { // Read from the first trellis board
prevMillisTrellis1 = currentMillis; // Save the current time
trellis1.read(); // Read and handle events from the first board
}
if (currentMillis - prevMillisTrellis2 >= INTERVAL) { // Read from the second trellis board
prevMillisTrellis2 = currentMillis; // Save the current time
trellis2.read(); // Read and handle events from the second board
}
}
// CLOUR WHEEL: Input a value 0 to 255 to get a color value. The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
if(WheelPos < 85) {
return trellis1.pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} else if(WheelPos < 170) {
WheelPos -= 85;
return trellis1.pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return trellis1.pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
return 0;
}
// ACTIVE TRELLIS SELECTION
void trellisModesSelection(){
// left trellis:
if (!leftDeck && !leftPad) {
trellis1_activeMode = deck0;
offset_Trellis1 = 0;
// Serial.println("Deck A active");
}
if (!leftDeck && leftPad) {
trellis1_activeMode = pad0;
offset_Trellis1 = 16;
// Serial.println("Pad A active");
}
if (leftDeck && !leftPad) {
trellis1_activeMode = deck1;
offset_Trellis1 = 32;
// Serial.println("Deck C active");
}
if (leftDeck && leftPad) {
trellis1_activeMode = pad1;
offset_Trellis1 = 48;
// Serial.println("Pad C active");
}
// right trellis:
if (!rightDeck && !rightPad) {
trellis2_activeMode = deck0;
offset_Trellis2 = 0;
// Serial.println("Deck A active");
}
if (!rightDeck && rightPad) {
trellis2_activeMode = pad0;
offset_Trellis2 = 16;
// Serial.println("Pad A active");
}
if (rightDeck && !rightPad) {
trellis2_activeMode = deck1;
offset_Trellis2 = 32;
// Serial.println("Deck C active");
}
if (rightDeck && rightPad) {
trellis2_activeMode = pad1;
offset_Trellis2 = 48;
// Serial.println("Pad C active");
}
}
// RECEIVING MIDI////////////////////////////////////////////////////////////////////////////////////////////////
void OnNoteOn(byte channel, byte note, byte velocity) {
if(channel == TRELLIS1_MIDI_CH){
int adjustedNote = note - MIDI_MSG_OFFSET; // this keeps into account that the note has been offset
uint32_t color = 0; // Placeholder for the color to set
// Determine the color based on the velocity value
if (velocity < 10) // LED OFF
color = (0);
else if (velocity == 10)
color = trellis1.pixels.Color(1, 1, 1); // white dim
else if (velocity == 11)
color = trellis1.pixels.Color(85, 85, 85); // white
else if (velocity == 12)
color = trellis1.pixels.Color(2, 0, 0); // red dim
else if (velocity == 13)
color = trellis1.pixels.Color(127, 0, 0); // red bright
else if (velocity == 14)
color = trellis1.pixels.Color(0, 1, 0); // green dim
else if (velocity == 15)
color = trellis1.pixels.Color(0, 127, 0); //green bright
else if (velocity == 16)
color = trellis1.pixels.Color(0, 0, 7); // blue dim
else if (velocity == 17)
color = trellis1.pixels.Color(0, 0, 127); // blue bright
else if (velocity == 18)
color = trellis1.pixels.Color(2, 2, 0); // yellow dim
else if (velocity == 19)
color = trellis1.pixels.Color(64, 64, 0); // yellow bright
else if (velocity == 20)
color = trellis1.pixels.Color(5, 2, 0); // Orange dim
else if (velocity == 21)
color = trellis1.pixels.Color(60, 12, 0); // Orange bright
else if (velocity == 22)
color = trellis1.pixels.Color(0, 4, 1); // Green Sea dim
else if (velocity == 23)
color = trellis1.pixels.Color(0, 127, 24); // Green Sea bright
else if (velocity == 24)
color = trellis1.pixels.Color(0, 0, 2); // Blue sky dim
else if (velocity == 25)
color = trellis1.pixels.Color(0, 127, 127); // Blue sky bright
else if (velocity == 26)
color = trellis1.pixels.Color(1, 0, 1); // Fuxia dim
else if (velocity == 27)
color = trellis1.pixels.Color(63, 0, 127); // Fuxia bright
else if (velocity > 27 && velocity <= 30) {
color = trellis1.pixels.Color(0, 0, 0); // LED off
}
else if (velocity >= 30 && velocity <= 40) { // Green with varying brightness
int brightness = map(velocity, 30, 40, 0, 255); // interpret velocity sent by traktor to determine color of pixel
color = trellis1.pixels.Color(0, brightness, 0); // Green color
}
else if (velocity > 40 && velocity <= 50) {
int brightness = map(velocity, 40, 50, 0, 255);
color = trellis1.pixels.Color(0, 0, brightness); // Blue color
}
else if (velocity > 50 && velocity <= 55) {
color = trellis1.pixels.Color(0, 0, 0); // LED off
}
else if (velocity > 55 && velocity <= 65) {
int brightness = map(velocity, 55, 65, 0, 255);
color = trellis1.pixels.Color(brightness, 0, 0); // Red color
}
else
color = trellis1.pixels.Color(127, 127, 127);
// Set the pixel color based on the active mode and adjusted note
switch(trellis1_activeMode){
case deck0:
if(adjustedNote < 16)
trellis1.pixels.setPixelColor((adjustedNote - offset_Trellis1), color);
break;
case pad0:
if((adjustedNote >= 16) && (adjustedNote < 32))
trellis1.pixels.setPixelColor((adjustedNote - offset_Trellis1), color);
break;
case deck1:
if((adjustedNote >= 32) && (adjustedNote < 48))
trellis1.pixels.setPixelColor((adjustedNote - offset_Trellis1), color);
break;
case pad1:
if((adjustedNote >= 48) && (adjustedNote < 64))
trellis1.pixels.setPixelColor((adjustedNote - offset_Trellis1), color);
break;
default:
return;
}
// Show the updated pixel colors
trellis1.pixels.show();
}
if(channel == TRELLIS2_MIDI_CH) {
int adjustedNote = note - MIDI_MSG_OFFSET; // this keeps into account that the note has been offset
uint32_t color = 0; // Placeholder for the color to set
// Determine the color based on the velocity value
if (velocity < 10) // LED OFF
color = (0);
else if (velocity == 10)
color = trellis2.pixels.Color(1, 1, 1); // white dim
else if (velocity == 11)
color = trellis2.pixels.Color(85, 85, 85); // white
else if (velocity == 12)
color = trellis2.pixels.Color(2, 0, 0); // red dim
else if (velocity == 13)
color = trellis2.pixels.Color(127, 0, 0); // red bright
else if (velocity == 14)
color = trellis2.pixels.Color(0, 1, 0); // green dim
else if (velocity == 15)
color = trellis2.pixels.Color(0, 127, 0); // green bright
else if (velocity == 16)
color = trellis2.pixels.Color(0, 0, 7); // blue dim
else if (velocity == 17)
color = trellis2.pixels.Color(0, 0, 127); // blue bright
else if (velocity == 18)
color = trellis2.pixels.Color(2, 2, 0); // yellow dim
else if (velocity == 19)
color = trellis2.pixels.Color(64, 64, 0); // yellow bright
else if (velocity == 20)
color = trellis2.pixels.Color(5, 2, 0); // Orange dim
else if (velocity == 21)
color = trellis2.pixels.Color(60, 12, 0); // Orange bright
else if (velocity == 22)
color = trellis2.pixels.Color(0, 4, 1); // Green Sea dim
else if (velocity == 23)
color = trellis2.pixels.Color(0, 127, 24); // Green Sea bright
else if (velocity == 24)
color = trellis2.pixels.Color(0, 0, 2); // Blue sky dim
else if (velocity == 25)
color = trellis2.pixels.Color(0, 127, 127); // Blue sky bright
else if (velocity == 26)
color = trellis2.pixels.Color(1, 0, 1); // Fuxia dim
else if (velocity == 27)
color = trellis2.pixels.Color(63, 0, 127); // Fuxia bright
else if (velocity > 27 && velocity <= 30) {
color = trellis2.pixels.Color(0, 0, 0); // LED off
}
else if (velocity >= 30 && velocity <= 40) { // Green with varying brightness
int brightness = map(velocity, 30, 40, 0, 255); // interpret velocity sent by traktor to determine color of pixel
color = trellis2.pixels.Color(0, brightness, 0); // Green color
}
else if (velocity > 40 && velocity <= 50) {
int brightness = map(velocity, 40, 50, 0, 255);
color = trellis2.pixels.Color(0, 0, brightness); // Blue color
}
else if (velocity > 50 && velocity <= 55) {
color = trellis2.pixels.Color(0, 0, 0); // LED off
}
else if (velocity > 55 && velocity <= 65) {
int brightness = map(velocity, 55, 65, 0, 255);
color = trellis2.pixels.Color(brightness, 0, 0); // Red color
}
else
color = trellis2.pixels.Color(127, 127, 127);
// Set the pixel color based on the active mode and adjusted note
switch(trellis2_activeMode) {
case deck0:
if(adjustedNote < 16)
trellis2.pixels.setPixelColor((adjustedNote - offset_Trellis2), color);
break;
case pad0:
if((adjustedNote >= 16) && (adjustedNote < 32))
trellis2.pixels.setPixelColor((adjustedNote - offset_Trellis2), color);
break;
case deck1:
if((adjustedNote >= 32) && (adjustedNote < 48))
trellis2.pixels.setPixelColor((adjustedNote - offset_Trellis2), color);
break;
case pad1:
if((adjustedNote >= 48) && (adjustedNote < 64))
trellis2.pixels.setPixelColor((adjustedNote - offset_Trellis2), color);
break;
default:
return;
}
// Show the updated pixel colors
trellis2.pixels.show();
}
if(channel == LED_BUTTON_MIDI_CH){
switch(note){
case 27: sr.set(0, HIGH); break;
case 23: sr.set(1, HIGH); break;
case 36: sr.set(2, HIGH); break;
case 31: sr.set(3, HIGH); break;
case 15: sr.set(4, HIGH); break;
case 21: sr.set(5, HIGH); break;
case 34: sr.set(6, HIGH); break;
case 16: sr.set(7, HIGH); break;
case 20: sr.set(8, HIGH); break;
case 14: sr.set(9, HIGH); break;
case 19: sr.set(10, HIGH); break;
case 43: sr.set(11, HIGH); break;
case 42: sr.set(12, HIGH); break;
case 41: sr.set(13, HIGH); break;
case 30: sr.set(14, HIGH); break;
case 40: sr.set(15, HIGH); break;
default:
return;
}
}
}
void OnNoteOff(byte channel, byte note, byte velocity) {
if(channel == TRELLIS1_MIDI_CH){
int adjustedNote = note - MIDI_MSG_OFFSET; // this keeps into account that the note has been offset
switch(trellis1_activeMode){
case deck0:
if(adjustedNote < 16)
trellis1.pixels.setPixelColor((adjustedNote - offset_Trellis1), 0, 0, 0);
break;
case pad0:
if((adjustedNote >= 16) && (adjustedNote < 32))
trellis1.pixels.setPixelColor((adjustedNote - offset_Trellis1), 0, 0, 0);
break;
case deck1:
if((adjustedNote >= 32) && (adjustedNote < 48))
trellis1.pixels.setPixelColor((adjustedNote - offset_Trellis1), 0, 0, 0);
break;
case pad1:
if((adjustedNote >= 48) && (adjustedNote < 64))
trellis1.pixels.setPixelColor((adjustedNote - offset_Trellis1), 0, 0, 0);
break;
default:
return;
}
trellis1.pixels.show();
}
if(channel == TRELLIS2_MIDI_CH){
int adjustedNote = note - MIDI_MSG_OFFSET; // this keeps into account that the note has been offset
switch(trellis2_activeMode){
case deck0:
if(adjustedNote < 16)
trellis2.pixels.setPixelColor((adjustedNote - offset_Trellis2), 0, 0, 0);
break;
case pad0:
if((adjustedNote >= 16) && (adjustedNote < 32))
trellis2.pixels.setPixelColor((adjustedNote - offset_Trellis2), 0, 0, 0);
break;
case deck1:
if((adjustedNote >= 32) && (adjustedNote < 48))
trellis2.pixels.setPixelColor((adjustedNote - offset_Trellis2), 0, 0, 0);
break;
case pad1:
if((adjustedNote >= 48) && (adjustedNote < 64))
trellis2.pixels.setPixelColor((adjustedNote - offset_Trellis2), 0, 0, 0);
break;
default:
return;
}
trellis2.pixels.show();
}
if(channel == LED_BUTTON_MIDI_CH){
switch(note){
case 27: sr.set(0, LOW); break;
case 23: sr.set(1, LOW); break;
case 36: sr.set(2, LOW); break;
case 31: sr.set(3, LOW); break;
case 15: sr.set(4, LOW); break;
case 21: sr.set(5, LOW); break;
case 34: sr.set(6, LOW); break;
case 16: sr.set(7, LOW); break;
case 20: sr.set(8, LOW); break;
case 14: sr.set(9, LOW); break;
case 19: sr.set(10, LOW); break;
case 43: sr.set(11, LOW); break;
case 42: sr.set(12, LOW); break;
case 41: sr.set(13, LOW); break;
case 30: sr.set(14, LOW); break;
case 40: sr.set(15, LOW); break;
default:
return;
}
}
}
void leftLCDmodeSpy(){
switch(trellis1_activeMode){
case deck0:
digitalWrite(extraLedL_pin, HIGH);
offset_Trellis1 = 0; // offset for the trellis modes ie. DECK A = 0, PAD A 16, DECK C = 32, PAD C 48
usbMIDI.sendNoteOn(127, 127, LED_BUTTON_MIDI_CH); // midi message to be assigned to Global - SendMonitorState in Traktor to refresh trellis LEDs
#ifdef DEBUG
Serial.println(F("DECK A"));
#endif
break;
case pad0:
digitalWrite(extraLedL_pin, HIGH);
offset_Trellis1 = 16; // offset for the trellis modes ie. DECK A = 0, PAD A 16, DECK C = 32, PAD C 48
usbMIDI.sendNoteOn(127, 127, LED_BUTTON_MIDI_CH); // midi message to be assigned to Global - SendMonitorState in Traktor to refresh trellis LEDs
#ifdef DEBUG
Serial.println(F("PAD A"));
#endif
break;
case deck1:
digitalWrite(extraLedL_pin, LOW);
offset_Trellis1 = 32; // offset for the trellis modes ie. DECK A = 0, PAD A 16, DECK C = 32, PAD C 48
usbMIDI.sendNoteOn(127, 127, LED_BUTTON_MIDI_CH); // midi message to be assigned to Global - SendMonitorState in Traktor to refresh trellis LEDs
#ifdef DEBUG
Serial.println(F("DECK C"));
#endif
break;
case pad1:
digitalWrite(extraLedL_pin, LOW);
offset_Trellis1 = 48; // offset for the trellis modes ie. DECK A = 0, PAD A 16, DECK C = 32, PAD C 48
usbMIDI.sendNoteOn(127, 127, LED_BUTTON_MIDI_CH); // midi message to be assigned to Global - SendMonitorState in Traktor to refresh trellis LEDs
#ifdef DEBUG
Serial.println(F("PAD C"));
#endif
break;
default:
return;
}
}
void rightLCDmodeSpy(){
switch(trellis2_activeMode){
case deck0:
digitalWrite(extraLedR_pin, HIGH);
offset_Trellis2 = 0; // offset for the trellis modes ie. DECK B = 0, PAD B 16, DECK D = 32, PAD B 48
usbMIDI.sendNoteOn(127, 127, LED_BUTTON_MIDI_CH); // midi message to be assigned to Global - SendMonitorState in Traktor to refresh trellis LEDs
#ifdef DEBUG
Serial.println(F("DECK B"));
#endif
break;
case pad0:
digitalWrite(extraLedR_pin, HIGH);
offset_Trellis2 = 16; // offset for the trellis modes ie. DECK B = 0, PAD B 16, DECK D = 32, PAD B 48
usbMIDI.sendNoteOn(127, 127, LED_BUTTON_MIDI_CH); // midi message to be assigned to Global - SendMonitorState in Traktor to refresh trellis LEDs
#ifdef DEBUG
Serial.println(F("PAD B"));
#endif
break;
case deck1:
digitalWrite(extraLedR_pin, LOW);
offset_Trellis2 = 32; // offset for the trellis modes ie. DECK B = 0, PAD B 16, DECK D = 32, PAD B 48
usbMIDI.sendNoteOn(127, 127, LED_BUTTON_MIDI_CH); // midi message to be assigned to Global - SendMonitorState in Traktor to refresh trellis LEDs
#ifdef DEBUG
Serial.println(F("DECK D"));
#endif
break;
case pad1:
digitalWrite(extraLedR_pin, LOW);
offset_Trellis2 = 48; // offset for the trellis modes ie. DECK B = 0, PAD B 16, DECK D = 32, PAD B 48
usbMIDI.sendNoteOn(127, 127, LED_BUTTON_MIDI_CH); // midi message to be assigned to Global - SendMonitorState in Traktor to refresh trellis LEDs
#ifdef DEBUG
Serial.println(F("PAD D"));
#endif
break;
default:
return;
}
}
The funny thing is that in order to post the code I copied all the single tabs in on efile and now the arduino IDE compile it without errors. Is this something that could be due to the file being split ? It's really odd. ... if I add this line at the beginning of the script:
"#define DEBUG_BUTTONS" in the file with separated tabs (the one posted above), it causes problems, the behaviour is so odd that the file will not compile even if I then delete the line, but it will if I undo until before writing the line
.....