#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// --- Hardware Mappings ---
const int NUM_POTS = 5;
const int potPins[NUM_POTS] = {A0, A1, A2, A3, A6};
const char* potNames[NUM_POTS] = {"MAIN", "CHROME", "SPOTIFY", "GAME", "DISCORD"};
const int NUM_BUTTONS = 6;
const int btnPins[NUM_BUTTONS] = {2, 3, 5, 4, 7, 6};
// --- Macro System States ---
bool micMuted = false;
bool recording = false;
int currentOutDev = 0; // 0 = Headphones, 1 = Speakers
int currentInDev = 0; // 0 = Headset Mic, 1 = Desk Mic
String lastActionText = "SYSTEM READY";
unsigned long actionTimer = 0;
// --- RGB Status LED Pins ---
const int LED_R = 9;
const int LED_G = 10;
const int LED_B = 11;
int potValues[NUM_POTS];
bool lastBtnState[NUM_BUTTONS];
void setup() {
Serial.begin(9600);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
for(;;);
}
for (int i = 0; i < NUM_BUTTONS; i++) {
pinMode(btnPins[i], INPUT_PULLUP);
lastBtnState[i] = HIGH;
}
pinMode(LED_R, OUTPUT);
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
updateLEDStatus();
}
void loop() {
readPotentiometers();
checkButtons();
updateOLED();
sendDeejData();
// Clear the temporary action text alert after 2 seconds
if (millis() - actionTimer > 2000 && lastActionText != "SYSTEM READY") {
lastActionText = "SYSTEM READY";
}
delay(10);
}
void readPotentiometers() {
for (int i = 0; i < NUM_POTS; i++) {
potValues[i] = analogRead(potPins[i]);
}
}
void checkButtons() {
for (int i = 0; i < NUM_BUTTONS; i++) {
bool currentState = digitalRead(btnPins[i]);
// Detect Button Press (Falling Edge)
if (currentState == LOW && lastBtnState[i] == HIGH) {
actionTimer = millis();
switch(i) {
case 0: // MUTE MIC
micMuted = !micMuted;
lastActionText = micMuted ? "MIC MUTED!" : "MIC UNMUTED";
Serial.println("MACRO:MUTE_MIC");
break;
case 1: // REC TOGGLE
recording = !recording;
lastActionText = recording ? "RECORDING STARTED" : "RECORDING STOPPED";
Serial.println("MACRO:TOGGLE_REC");
break;
case 2: // CLIP AUD
lastActionText = "SAVED AUDIO CLIP";
Serial.println("MACRO:CLIP_AUDIO");
break;
case 3: // CLIP V+A
lastActionText = "SAVED VIDEO+AUDIO";
Serial.println("MACRO:CLIP_VIDEO");
break;
case 4: // IN DEV
currentInDev = (currentInDev == 0) ? 1 : 0;
lastActionText = (currentInDev == 0) ? "IN: HEADSET MIC" : "IN: DESK MIC";
Serial.println("MACRO:SWITCH_INPUT");
break;
case 5: // OUT DEV
currentOutDev = (currentOutDev == 0) ? 1 : 0;
lastActionText = (currentOutDev == 0) ? "OUT: HEADPHONES" : "OUT: SPEAKERS";
Serial.println("MACRO:SWITCH_OUTPUT");
break;
}
updateLEDStatus();
delay(150); // Harder debounce to avoid double-triggers
}
lastBtnState[i] = currentState;
}
}
// --- Dynamic RGB Profile Logic ---
void updateLEDStatus() {
if (micMuted) {
// Red Alert for Muted Microphone
analogWrite(LED_R, 255); analogWrite(LED_G, 0); analogWrite(LED_B, 0);
} else if (recording) {
// Purple blinking flavor for Recording active
analogWrite(LED_R, 150); analogWrite(LED_G, 0); analogWrite(LED_B, 255);
} else {
// Solid clean Cyan for normal mix operations
analogWrite(LED_R, 0); analogWrite(LED_G, 200); analogWrite(LED_B, 255);
}
}
void updateOLED() {
display.clearDisplay();
// Header Panel
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("STATUS: ");
display.println(lastActionText);
display.drawFastHLine(0, 9, 128, SSD1306_WHITE);
// Render Custom Volume Sliders Side-by-Side Vertically
// This packs all 5 channels cleanly across the 128-pixel width screen
for (int i = 0; i < NUM_POTS; i++) {
int colLeft = i * 26; // Dynamic horizontal layout slots
// Draw the tiny name identifier labels at the bottom row
display.setCursor(colLeft + 2, 56);
display.print(potNames[i][0]); // Prints first letter: M, C, S, G, D
// Scale pot reading (0-1023) to screen pixel height (0-40 pixels max tall)
int barHeight = map(potValues[i], 0, 1023, 0, 40);
// Draw the empty channel slider bounds box
display.drawRect(colLeft + 4, 13, 14, 42, SSD1306_WHITE);
// Fill the inner volume indicator from the bottom upwards
display.fillRect(colLeft + 6, 13 + (40 - barHeight), 10, barHeight, SSD1306_WHITE);
}
display.display();
}
void sendDeejData() {
for (int i = 0; i < NUM_POTS; i++) {
Serial.print(potValues[i]);
if (i < NUM_POTS - 1) {
Serial.print("|");
}
}
Serial.println();
}