#include <SevenSegmentTM1637.h>
// =========================
// PIN CONFIG
// =========================
const byte RED_PIN = 9;
const byte GREEN_PIN = 10;
const byte BLUE_PIN = 11;
const byte WHITE_PIN = 3;
const byte BTN_MENU = A3;
const byte BTN_UP = A2;
const byte BTN_DOWN = A1;
const byte BTN_ENTER = A0;
const byte SOUND_PIN = A4;
// TM1637 pins
const byte TM_CLK = 6;
const byte TM_DIO = 7;
SevenSegmentTM1637 display(TM_CLK, TM_DIO);
// =========================
// MODES
// =========================
enum Mode {
MODE_STATIC = 0,
MODE_FADE,
MODE_STROBE,
MODE_LIGHTNING,
MODE_SOUND,
MODE_COUNT
};
const char* modeNames[] = {
"STATIC",
"FADE",
"STROBE",
"LIGHTNING",
"SOUND"
};
// menu item:
// P = program/mode
// S = speed
// C = color/intensity
// U = sound sensitivity
enum MenuItem {
ITEM_PROGRAM = 0,
ITEM_SPEED,
ITEM_COLOR,
ITEM_SOUND,
ITEM_COUNT
};
int currentMode = MODE_STATIC;
MenuItem currentItem = ITEM_PROGRAM;
bool effectRunning = true;
bool editMode = false;
// =========================
// EFFECT PARAMETERS
// =========================
// STATIC mode
int staticColor = 1; // 1..8
// FADE mode
int fadeSpeed = 20; // 5..100, lower=faster
int fadeValue = 0;
int fadeStep = 5;
// STROBE mode
int strobeSpeed = 80; // 20..300
bool strobeOn = false;
// LIGHTNING mode
int lightningIntensity = 2; // 0..3
bool lightningFlashOn = false;
unsigned long lightningTimer = 0;
int lightningPhase = 0;
int lightningBursts = 0;
int lightningPause = 0;
// SOUND mode
int soundSensitivity = 30; // 0..200
// =========================
// BUTTON STATES
// =========================
bool lastMenuState = LOW;
bool lastUpState = LOW;
bool lastDownState = LOW;
bool lastEnterState = LOW;
// =========================
// TIMERS
// =========================
unsigned long lastPrint = 0;
unsigned long lastEffect = 0;
unsigned long blinkTimer = 0;
bool blinkVisible = true;
// =========================
// HELPERS
// =========================
void setRGBW(int r, int g, int b, int w) {
analogWrite(RED_PIN, constrain(r, 0, 255));
analogWrite(GREEN_PIN, constrain(g, 0, 255));
analogWrite(BLUE_PIN, constrain(b, 0, 255));
analogWrite(WHITE_PIN, constrain(w, 0, 255));
}
void ledsOff() {
setRGBW(0, 0, 0, 0);
}
const char* getStaticColorName() {
switch (staticColor) {
case 1: return "RED";
case 2: return "GREEN";
case 3: return "BLUE";
case 4: return "WHITE";
case 5: return "YELLOW";
case 6: return "CYAN";
case 7: return "MAGENTA";
case 8: return "FULL WHITE";
}
return "UNKNOWN";
}
// =========================
// SERIAL STATUS
// =========================
void printStatus() {
Serial.print("Menu: ");
switch (currentItem) {
case ITEM_PROGRAM: Serial.print("PROGRAM"); break;
case ITEM_SPEED: Serial.print("SPEED"); break;
case ITEM_COLOR: Serial.print("COLOR/INT"); break;
case ITEM_SOUND: Serial.print("SENSITIVITY"); break;
}
Serial.print(" | Edit: ");
Serial.print(editMode ? "YES" : "NO");
Serial.print(" | Mode: ");
Serial.print(modeNames[currentMode]);
Serial.print(" | Running: ");
Serial.print(effectRunning ? "YES" : "NO");
Serial.print(" | Value: ");
switch (currentItem) {
case ITEM_PROGRAM:
Serial.print(currentMode + 1);
break;
case ITEM_SPEED:
if (currentMode == MODE_FADE) {
Serial.print("FADE Speed ");
Serial.print(fadeSpeed);
} else if (currentMode == MODE_STROBE) {
Serial.print("STROBE Speed ");
Serial.print(strobeSpeed);
} else {
Serial.print("N/A in this mode");
}
break;
case ITEM_COLOR:
if (currentMode == MODE_STATIC) {
Serial.print(getStaticColorName());
Serial.print(" (");
Serial.print(staticColor);
Serial.print(")");
} else if (currentMode == MODE_LIGHTNING) {
Serial.print("Lightning Intensity ");
Serial.print(lightningIntensity);
} else {
Serial.print("N/A in this mode");
}
break;
case ITEM_SOUND:
if (currentMode == MODE_SOUND) {
Serial.print(soundSensitivity);
} else {
Serial.print("N/A in this mode");
}
break;
}
Serial.println();
}
// =========================
// DISPLAY HELPERS
// =========================
void displayRaw4(const char *txt) {
// library SevenSegmentTM1637 mendukung print string pendek
display.clear();
display.print(txt);
}
void makeDisplayText(char prefix, int value, char *out) {
if (value < 0) value = 0;
if (value > 999) value = 999;
out[0] = prefix;
out[1] = '0' + ((value / 100) % 10);
out[2] = '0' + ((value / 10) % 10);
out[3] = '0' + (value % 10);
out[4] = '\0';
}
int getDisplayValue() {
switch (currentItem) {
case ITEM_PROGRAM:
return currentMode + 1;
case ITEM_SPEED:
if (currentMode == MODE_FADE) return fadeSpeed;
if (currentMode == MODE_STROBE) return strobeSpeed;
return 0;
case ITEM_COLOR:
if (currentMode == MODE_STATIC) return staticColor;
if (currentMode == MODE_LIGHTNING) return lightningIntensity;
return 0;
case ITEM_SOUND:
if (currentMode == MODE_SOUND) return soundSensitivity;
return 0;
}
return 0;
}
char getDisplayPrefix() {
switch (currentItem) {
case ITEM_PROGRAM: return 'P';
case ITEM_SPEED: return 'S';
case ITEM_COLOR: return 'C';
case ITEM_SOUND: return 'U';
}
return 'P';
}
void updateDisplay() {
if (editMode) {
if (millis() - blinkTimer >= 300) {
blinkTimer = millis();
blinkVisible = !blinkVisible;
}
if (!blinkVisible) {
display.clear();
return;
}
} else {
blinkVisible = true;
}
char txt[5];
makeDisplayText(getDisplayPrefix(), getDisplayValue(), txt);
displayRaw4(txt);
}
// =========================
// BUTTON HELPER
// =========================
bool pressedNow(byte pin, bool &lastState) {
bool currentState = digitalRead(pin);
bool pressed = (currentState == HIGH && lastState == LOW);
lastState = currentState;
return pressed;
}
// =========================
// VALUE ADJUSTMENTS
// =========================
void adjustCurrentUp() {
switch (currentItem) {
case ITEM_PROGRAM:
currentMode++;
if (currentMode >= MODE_COUNT) currentMode = 0;
break;
case ITEM_SPEED:
if (currentMode == MODE_FADE) {
fadeSpeed += 5;
if (fadeSpeed > 100) fadeSpeed = 100;
} else if (currentMode == MODE_STROBE) {
strobeSpeed += 10;
if (strobeSpeed > 300) strobeSpeed = 300;
}
break;
case ITEM_COLOR:
if (currentMode == MODE_STATIC) {
staticColor++;
if (staticColor > 8) staticColor = 1;
} else if (currentMode == MODE_LIGHTNING) {
lightningIntensity++;
if (lightningIntensity > 3) lightningIntensity = 3;
}
break;
case ITEM_SOUND:
if (currentMode == MODE_SOUND) {
soundSensitivity += 5;
if (soundSensitivity > 200) soundSensitivity = 200;
}
break;
}
}
void adjustCurrentDown() {
switch (currentItem) {
case ITEM_PROGRAM:
currentMode--;
if (currentMode < 0) currentMode = MODE_COUNT - 1;
break;
case ITEM_SPEED:
if (currentMode == MODE_FADE) {
fadeSpeed -= 5;
if (fadeSpeed < 5) fadeSpeed = 5;
} else if (currentMode == MODE_STROBE) {
strobeSpeed -= 10;
if (strobeSpeed < 20) strobeSpeed = 20;
}
break;
case ITEM_COLOR:
if (currentMode == MODE_STATIC) {
staticColor--;
if (staticColor < 1) staticColor = 8;
} else if (currentMode == MODE_LIGHTNING) {
lightningIntensity--;
if (lightningIntensity < 0) lightningIntensity = 0;
}
break;
case ITEM_SOUND:
if (currentMode == MODE_SOUND) {
soundSensitivity -= 5;
if (soundSensitivity < 0) soundSensitivity = 0;
}
break;
}
}
// =========================
// BUTTON HANDLER
// =========================
void handleButtons() {
if (pressedNow(BTN_MENU, lastMenuState)) {
if (!editMode) {
currentItem = (MenuItem)(currentItem + 1);
if (currentItem >= ITEM_COUNT) currentItem = ITEM_PROGRAM;
printStatus();
}
delay(150);
}
if (pressedNow(BTN_ENTER, lastEnterState)) {
editMode = !editMode;
blinkVisible = true;
blinkTimer = millis();
printStatus();
delay(150);
}
if (pressedNow(BTN_UP, lastUpState)) {
if (editMode) {
adjustCurrentUp();
printStatus();
}
delay(150);
}
if (pressedNow(BTN_DOWN, lastDownState)) {
if (editMode) {
adjustCurrentDown();
printStatus();
}
delay(150);
}
}
// =========================
// EFFECTS
// =========================
void runStaticMode() {
switch (staticColor) {
case 1: setRGBW(255, 0, 0, 0); break;
case 2: setRGBW(0, 255, 0, 0); break;
case 3: setRGBW(0, 0, 255, 0); break;
case 4: setRGBW(0, 0, 0, 255); break;
case 5: setRGBW(255, 180, 0, 0); break;
case 6: setRGBW(0, 255, 255, 0); break;
case 7: setRGBW(255, 0, 255, 0); break;
case 8: setRGBW(255, 255, 255, 255); break;
}
}
void runFadeMode() {
if (millis() - lastEffect < (unsigned long)fadeSpeed) return;
lastEffect = millis();
fadeValue += fadeStep;
if (fadeValue >= 255) {
fadeValue = 255;
fadeStep = -fadeStep;
}
if (fadeValue <= 0) {
fadeValue = 0;
fadeStep = -fadeStep;
}
setRGBW(fadeValue, 0, 255 - fadeValue, fadeValue / 2);
}
void runStrobeMode() {
if (millis() - lastEffect < (unsigned long)strobeSpeed) return;
lastEffect = millis();
strobeOn = !strobeOn;
if (strobeOn) {
setRGBW(255, 255, 255, 255);
} else {
ledsOff();
}
}
void startLightningSequence() {
lightningPhase = 0;
lightningFlashOn = false;
lightningBursts = random(2, 6);
lightningPause = random(400, 1500);
lightningTimer = millis();
}
void runLightningMode() {
unsigned long now = millis();
if (lightningPhase == 0) {
if (now - lightningTimer >= (unsigned long)lightningPause) {
lightningPhase = 1;
lightningTimer = now;
switch (lightningIntensity) {
case 0: lightningBursts = random(1, 3); break;
case 1: lightningBursts = random(2, 4); break;
case 2: lightningBursts = random(3, 6); break;
case 3: lightningBursts = random(5, 9); break;
}
}
return;
}
if (lightningPhase == 1) {
if (now - lightningTimer >= 40) {
lightningTimer = now;
lightningFlashOn = !lightningFlashOn;
if (lightningFlashOn) {
switch (lightningIntensity) {
case 0: setRGBW(0, 0, 50, 120); break;
case 1: setRGBW(40, 40, 180, 180); break;
case 2: setRGBW(80, 80, 255, 220); break;
case 3: setRGBW(180, 180, 255, 255); break;
}
} else {
ledsOff();
lightningBursts--;
if (lightningBursts <= 0) {
lightningPhase = 0;
lightningPause = random(700, 2500);
lightningTimer = now;
}
}
}
}
}
void runSoundMode() {
int sensor = analogRead(SOUND_PIN);
int level = map(sensor, 0, 1023, 0, 255);
if (level < soundSensitivity) {
ledsOff();
} else if (level < 80) {
setRGBW(0, 0, level, 0);
} else if (level < 140) {
setRGBW(0, level, 255 - level / 2, 0);
} else if (level < 220) {
setRGBW(level, level / 2, 0, 0);
} else {
setRGBW(255, 255, 255, level);
}
}
void runCurrentMode() {
switch (currentMode) {
case MODE_STATIC: runStaticMode(); break;
case MODE_FADE: runFadeMode(); break;
case MODE_STROBE: runStrobeMode(); break;
case MODE_LIGHTNING: runLightningMode(); break;
case MODE_SOUND: runSoundMode(); break;
}
}
// =========================
// SETUP
// =========================
void setup() {
Serial.begin(115200);
randomSeed(analogRead(A5));
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
pinMode(WHITE_PIN, OUTPUT);
pinMode(BTN_MENU, INPUT);
pinMode(BTN_UP, INPUT);
pinMode(BTN_DOWN, INPUT);
pinMode(BTN_ENTER, INPUT);
ledsOff();
startLightningSequence();
display.begin();
display.setBacklight(100);
display.clear();
Serial.println("RGBW PAR LIGHT READY");
Serial.println("MENU = pindah item menu");
Serial.println("ENTER = masuk/simpan edit");
Serial.println("UP/DOWN = ubah nilai saat edit");
printStatus();
setRGBW(255, 0, 0, 0); delay(250);
setRGBW(0, 255, 0, 0); delay(250);
setRGBW(0, 0, 255, 0); delay(250);
setRGBW(0, 0, 0, 255); delay(250);
ledsOff();
}
// =========================
// LOOP
// =========================
void loop() {
handleButtons();
updateDisplay();
runCurrentMode();
//if (millis() - lastPrint > 3000) {
//lastPrint = millis();
// printStatus();
// }
}SOUND SENSOR
MENU
ENTER
DOWN
UP