#include <Wire.h>
#include <ButtonDebounce.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RotaryEncoder.h>
#include <L293D.h>
#include <TCA9548A.h>
// Setup Buttons
// #define DebounceTime 250 // in milliseconds
// ButtonDebounce btn_MuteAll (1, DebounceTime);
// ButtonDebounce btn_MuteMic (2, DebounceTime);
// ButtonDebounce btn_Mute1 (3, DebounceTime);
// ButtonDebounce btn_Mute2 (4, DebounceTime);
// ButtonDebounce btn_Mute3 (5, DebounceTime);
// ButtonDebounce btn_Source1 (6, DebounceTime);
// ButtonDebounce btn_Source2 (7, DebounceTime);
// ButtonDebounce btn_Source3 (8, DebounceTime);
// ButtonDebounce btn_Source4 (9, DebounceTime);
#define MUTE_ALL 11
#define MASTER_UP 12
#define MASTER_DOWN 13
#define MUTE_MIC 2
#define SOURCE_1 6
#define SOURCE_2 7
#define SOURCE_3 8
#define SOURCE_4 9
#define MUTE_1 3
#define MUTE_2 4
#define MUTE_3 5
#define NUM_BUTTONS 11
int buttonPins[NUM_BUTTONS] = {MUTE_ALL, MASTER_UP, MASTER_DOWN, MUTE_MIC, SOURCE_1, SOURCE_2, SOURCE_3, SOURCE_4, MUTE_1, MUTE_2, MUTE_3};
int lastButtonReadValue[NUM_BUTTONS] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH};
// Setup WS2812 LEDs
#define LED_DataPin 10
#define NumPixels 9
#define Brightness 50
Adafruit_NeoPixel pixels(NumPixels, LED_DataPin, NEO_GRB + NEO_KHZ800);
//Setup Displays
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3D
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display1(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_SSD1306 display2(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_SSD1306 display3(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Setup a RotaryEncoder with 2 steps per latch for the 2 signal input pins:
#define ENCODER_CLK 31
#define ENCODER_DT 30
// #define ROTARYSTEPS 2
// #define ROTARYMIN 0
// #define ROTARYMAX 16
// RotaryEncoder MasterVolEncoder(ENCODER_CLK, ENCODER_DT, RotaryEncoder::LatchMode::TWO03);
// long lastMasterVolPosition = 0;
int lastMasterVolCLKPin = HIGH;
//Setup L293D Motor Controllers
#define MOTOR1_EN 33
#define MOTOR1_FOR 34
#define MOTOR1_REV 35
#define MOTOR2_EN 36
#define MOTOR2_FOR 37
#define MOTOR2_REV 38
#define MOTOR3_EN 39
#define MOTOR3_FOR 40
#define MOTOR3_REV 41
L293D motor1(MOTOR1_FOR, MOTOR1_REV, MOTOR1_EN);
L293D motor2(MOTOR2_FOR, MOTOR2_REV, MOTOR2_EN);
L293D motor3(MOTOR3_FOR, MOTOR3_REV, MOTOR3_EN);
//Setup I2C Mux
TCA9548A I2CMux;
//Setup slides
#define SLIDE1 A0
#define SLIDE2 A1
#define SLIDE3 A2
// diagram changelog
// slide inputs moved from comm 14-16 to analog in 0-2
// color changed to brown so you can spot them and re-do the bendy bits
// moved mute all from mega:1 to mega:11 bc idk if serial works on the others
// added serial connection on mega:0 and mega:1
struct State {
int Priority;
bool MicMuted;
int AudioOutID;
int MasterVol;
bool MasterMuted;
int Slide1Vol;
bool Slide1Muted;
int Slide2Vol;
bool Slide2Muted;
int Slide3Vol;
bool Slide3Muted;
};
int loopCounter = 0;
State deejState;
State hardwareLastState;
bool stateChanged = false;
bool recievedFirstState = false;
void initializeState(State& state) {
state.Priority = 0;
state.MicMuted = false;
state.AudioOutID = 0;
state.MasterVol = 0;
state.MasterMuted = false;
state.Slide1Vol = 0;
state.Slide1Muted = false;
state.Slide2Vol = 0;
state.Slide2Muted = false;
state.Slide3Vol = 0;
state.Slide3Muted = false;
}
String serializeState(const State& state) {
return String(state.Priority) + "|" +
String(state.MicMuted) + "|" +
String(state.AudioOutID) + "|" +
String(state.MasterVol) + "|" +
String(state.MasterMuted) + "|" +
String(state.Slide1Vol) + "|" +
String(state.Slide1Muted) + "|" +
String(state.Slide2Vol) + "|" +
String(state.Slide2Muted) + "|" +
String(state.Slide3Vol) + "|" +
String(state.Slide3Muted);
}
// validates the format of a serialized state string
bool isValidFormat(const String input) {
Serial.println("Validating format of: " + input);
int pipeCount = 0;
for (char c : input) {
if (c == '|') {
pipeCount++;
}
}
// Check if there are exactly 10 pipes (which means 11 tokens)
if (pipeCount != 10) {
return false;
}
// Split the string by '|'
char *token = strtok(input.c_str(), "|");
while (token != NULL) {
// Check if each token is a number or "true" or "false"
if (strcmp(token, "true") != 0 && strcmp(token, "false") != 0) {
for (int i = 0; i < strlen(token); i++) {
if (!isdigit(token[i])) {
return false;
}
}
}
token = strtok(NULL, "|");
}
return true;
}
bool deserializeState(const String& input, State& state) {
if (!isValidFormat(input)) {
return false;
}
char* token = strtok(input.c_str(), "|");
state.Priority = atoi(token);
token = strtok(NULL, "|");
state.MicMuted = strcmp(token, "true") == 0;
token = strtok(NULL, "|");
state.AudioOutID = atoi(token);
token = strtok(NULL, "|");
state.MasterVol = atoi(token);
token = strtok(NULL, "|");
state.MasterMuted = strcmp(token, "true") == 0;
token = strtok(NULL, "|");
state.Slide1Vol = atoi(token);
token = strtok(NULL, "|");
state.Slide1Muted = strcmp(token, "true") == 0;
token = strtok(NULL, "|");
state.Slide2Vol = atoi(token);
token = strtok(NULL, "|");
state.Slide2Muted = strcmp(token, "true") == 0;
token = strtok(NULL, "|");
state.Slide3Vol = atoi(token);
token = strtok(NULL, "|");
state.Slide3Muted = strcmp(token, "true") == 0;
return true;
}
void sendState(const State& state) {
String serializedState = serializeState(state);
Serial.println(serializedState);
}
void pressMuteAll() {
hardwareLastState.MasterMuted = !hardwareLastState.MasterMuted;
stateChanged = true;
Serial.println("pressMuteAll");
}
void pressMasterUp() {
if (hardwareLastState.MasterVol < 100) {
hardwareLastState.MasterVol += 1;
stateChanged = true;
Serial.println("pressMasterUp");
}
}
void pressMasterDown() {
if (hardwareLastState.MasterVol > 0) {
hardwareLastState.MasterVol -= 1;
stateChanged = true;
Serial.println("pressMasterDown");
}
}
void pressMuteMic() {
hardwareLastState.MicMuted = !hardwareLastState.MicMuted;
stateChanged = true;
Serial.println("pressMuteMic");
}
void pressSlide1Mute() {
hardwareLastState.Slide1Muted = !hardwareLastState.Slide1Muted;
stateChanged = true;
Serial.println("pressSlide1Mute");
}
void pressSlide2Mute() {
hardwareLastState.Slide2Muted = !hardwareLastState.Slide2Muted;
stateChanged = true;
Serial.println("pressSlide2Mute");
}
void pressSlide3Mute() {
hardwareLastState.Slide3Muted = !hardwareLastState.Slide3Muted;
stateChanged = true;
Serial.println("pressSlide3Mute");
}
void pressSource1() {
hardwareLastState.AudioOutID = 0;
stateChanged = true;
Serial.println("pressSource1");
}
void pressSource2() {
hardwareLastState.AudioOutID = 1;
stateChanged = true;
Serial.println("pressSource2");
}
void pressSource3() {
hardwareLastState.AudioOutID = 2;
stateChanged = true;
Serial.println("pressSource3");
}
void pressSource4() {
hardwareLastState.AudioOutID = 3;
stateChanged = true;
Serial.println("pressSource4");
}
int readSlideValueAsIntegerPercentage(int slidePin) {
return static_cast<int>((static_cast<float>(analogRead(slidePin)) / 1023.0) * 100);
}
void readAndSendHardwareState() {
int slide1Value = readSlideValueAsIntegerPercentage(SLIDE1);
if (slide1Value != hardwareLastState.Slide1Vol) {
hardwareLastState.Slide1Vol = slide1Value;
stateChanged = true;
Serial.println("readAndSendHardwareState1");
}
int slide2Value = readSlideValueAsIntegerPercentage(SLIDE2);
if (slide2Value != hardwareLastState.Slide2Vol) {
hardwareLastState.Slide2Vol = slide2Value;
stateChanged = true;
Serial.println("readAndSendHardwareState2");
}
int slide3Value = readSlideValueAsIntegerPercentage(SLIDE3);
if (slide3Value != hardwareLastState.Slide3Vol) {
hardwareLastState.Slide3Vol = slide3Value;
stateChanged = true;
Serial.println("readAndSendHardwareState3");
}
if (stateChanged) {
stateChanged = false;
sendState(hardwareLastState);
}
}
// void setupButtonCallbacks()
// {
// btn_MuteAll.setCallback(pressMuteAll);
// btn_MuteMic.setCallback(pressMuteMic);
// btn_Mute1.setCallback(pressSlide1Mute);
// btn_Mute2.setCallback(pressSlide2Mute);
// btn_Mute3.setCallback(pressSlide3Mute);
// btn_Source1.setCallback(pressSource1);
// btn_Source2.setCallback(pressSource2);
// btn_Source3.setCallback(pressSource3);
// btn_Source4.setCallback(pressSource4);
// }
void setup() {
// put your setup code here, to run once:
pinMode(SLIDE1, INPUT);
pinMode(SLIDE2, INPUT);
pinMode(SLIDE3, INPUT);
pinMode(ENCODER_CLK, INPUT);
pinMode(ENCODER_DT, INPUT);
//setupButtonCallbacks();
for (int i = 0; i < NUM_BUTTONS; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
initializeState(deejState);
initializeState(hardwareLastState);
Serial.begin(9600);
I2CMux.begin(Wire);
I2CMux.closeAll();
pixels.begin();
pixels.show(); // Turn OFF all pixels ASAP
pixels.setBrightness(Brightness); //set default brightness
}
void updateHardwareState(const State& state) {
// master volume and mutes can be ignored for now as they are not reflected by the hardware components
if (state.Slide1Vol != hardwareLastState.Slide1Vol) {
// Set Slide 1 Volume
}
if (state.Slide2Vol != hardwareLastState.Slide2Vol) {
// Set Slide 2 Volume
}
if (state.Slide3Vol != hardwareLastState.Slide3Vol) {
// Set Slide 3 Volume
}
if (state.AudioOutID != hardwareLastState.AudioOutID) {
switch (state.AudioOutID) {
case 1:
// Set Audio Out to 1
break;
case 2:
// Set Audio Out to 2
break;
case 3:
// Set Audio Out to 3
break;
case 4:
// Set Audio Out to 4
break;
}
}
hardwareLastState = state;
}
bool compareStates(const State& state1, const State& state2) {
return state1.MicMuted == state2.MicMuted &&
state1.AudioOutID == state2.AudioOutID &&
state1.MasterVol == state2.MasterVol &&
state1.MasterMuted == state2.MasterMuted &&
state1.Slide1Vol == state2.Slide1Vol &&
state1.Slide1Muted == state2.Slide1Muted &&
state1.Slide2Vol == state2.Slide2Vol &&
state1.Slide2Muted == state2.Slide2Muted &&
state1.Slide3Vol == state2.Slide3Vol &&
state1.Slide3Muted == state2.Slide3Muted;
}
void printState(const State& state) {
Serial.println("State:");
Serial.print(" MicMuted: ");
Serial.println(state.MicMuted);
Serial.print(" AudioOutID: ");
Serial.println(state.AudioOutID);
Serial.print(" MasterVol: ");
Serial.println(state.MasterVol);
Serial.print(" MasterMuted: ");
Serial.println(state.MasterMuted);
Serial.print(" Slide1Vol: ");
Serial.println(state.Slide1Vol);
Serial.print(" Slide1Muted: ");
Serial.println(state.Slide1Muted);
Serial.print(" Slide2Vol: ");
Serial.println(state.Slide2Vol);
Serial.print(" Slide2Muted: ");
Serial.println(state.Slide2Muted);
Serial.print(" Slide3Vol: ");
Serial.println(state.Slide3Vol);
Serial.print(" Slide3Muted: ");
Serial.println(state.Slide3Muted);
}
void checkForNewDeejState() {
if (Serial.available()) {
Serial.println("Serial input detected.");
String incoming = Serial.readStringUntil('\n');
State newState;
if (deserializeState(incoming, newState)) {
if (!compareStates(newState, deejState)) {
Serial.println("Received new state:");
recievedFirstState = true;
printState(newState);
deejState = newState;
updateHardwareState(deejState);
}
}
}
}
void checkButtons() {
for (int i = 0; i < NUM_BUTTONS; i++) {
int pin = buttonPins[i];
int newButtonReadValue = digitalRead(pin);
if (newButtonReadValue != lastButtonReadValue[i]) {
lastButtonReadValue[i] = newButtonReadValue;
if (newButtonReadValue == LOW) {
switch (pin) {
case MUTE_ALL: pressMuteAll(); break;
case MASTER_UP: pressMasterUp(); break;
case MASTER_DOWN: pressMasterDown(); break;
case MUTE_MIC: pressMuteMic(); break;
case SOURCE_1: pressSource1(); break;
case SOURCE_2: pressSource2(); break;
case SOURCE_3: pressSource3(); break;
case SOURCE_4: pressSource4(); break;
case MUTE_1: pressSlide1Mute(); break;
case MUTE_2: pressSlide2Mute(); break;
case MUTE_3: pressSlide3Mute(); break;
}
}
}
}
}
void loop() {
checkButtons();
if (recievedFirstState) {
readAndSendHardwareState();
}
checkForNewDeejState();
delay(10);
}Mute All
Master Down
Master Up
Mute Mic
Mute 1
Mute 2
Mute 3
Source 1
Source 2
Source 3
Source 4
Motor1 Fwd
Motor1 Rev
Motor2 Fwd
Motor2 Rev
Motor3 Fwd
Motor3 Rev