/* RiTCh Lightshow Simple Arduino based MP3/DMX player which can be triggered by buttons. (c)2024 by H. Wirtz */ /* TODO: - */ #include #include #include "DmxSimple.h" #include #include "looper.h" #include #define DEBUG 1 #define SENSOR_SCHED 10 #define DMX_COLOR_CHANGER_SCHED 100 #define LED_SCHED 50 #define LEVEL_CHECK_SCHED 100 #define DEBUG_SCHED 500 #define BUTTON_LONG_PRESS 1000 #define MIN_TIME_SWITCH_PRESSED 50 #define POTI_DEAD_ZONE 3 #define LED_NORMAL_BRIGHTNESS 180 #define LED_PLAY_BRIGHTNESS 20 #define DMX_MAX_CHANNEL 512 #define MAX_VOL_LEVEL 30 #define MAX_DMX_LEVEL 255 #define DMX_FOG_MACHINE_ADDRESS 100 #define MAX_RAND_WHITE_LEVEL 128 #define DMX_BRIGHTNESS 0 #define DMX_FADE_TIME 1 #define DMX_HOLD_TIME 2 #define DMX_FOG_LEVEL 3 #define MIN_DMX_FADE_TIME 1 #define MAX_DMX_FADE_TIME 15 #define MIN_DMX_HOLD_TIME 3 #define MAX_DMX_HOLD_TIME 30 #define ON_OFF_FADE_TIME 1 // Arduino pins #define POTI1_PIN A1 #define POTI2_PIN A2 #define POTI3_PIN A3 #define POTI4_PIN A4 #define BUTTON1_PIN 4 #define BUTTON2_PIN 5 #define BUTTON3_PIN 6 #define BUTTON4_PIN 7 #define DMX_PIN 8 #define LED_PIN 9 #define MP3_RX_PIN 10 #define MP3_TX_PIN 11 #define BUSY_PIN 12 uint8_t button_state; uint32_t button_time[4] = { 0, 0, 0, 0 }; uint8_t poti_level[4] = { 0, 0, 0, 0 }; uint8_t brightness = LED_NORMAL_BRIGHTNESS; enum { NORMAL, WHITE, OFF }; bool fog_state = false; uint8_t light_state = NORMAL; typedef struct { uint8_t dmx_type = 4; uint16_t address = 0; uint16_t steps = 0; float diff[4] = { 0.0, 0.0, 0.0, 0.0 }; float values[4] = { 0.0, 0.0, 0.0, 0.0 }; } dmx_spot; #define MAX_DMX_SPOTS 2 dmx_spot spot[MAX_DMX_SPOTS]; // schedular looper sched; // setup audio card SoftwareSerial mySoftwareSerial(MP3_RX_PIN, MP3_TX_PIN); // RX, TX DFRobotDFPlayerMini myDFPlayer; //-------------------------------------------------------------------------------- // THREADS //-------------------------------------------------------------------------------- void button_check(void) { byte i; for (i = 0; i < 4; i++) { uint16_t b = button(i); if (b > BUTTON_LONG_PRESS && !bitRead(button_state, i)) { // long press #ifdef DEBUG Serial.print(F("Button[")); Serial.print(i + 1, DEC); Serial.println(F("]: long")); #endif button_time[i] = 0; do_button_long(i); } else if (b > MIN_TIME_SWITCH_PRESSED && !bitRead(button_state, i)) { // short press #ifdef DEBUG Serial.print(F("Button[")); Serial.print(i + 1, DEC); Serial.println(F("]: short")); #endif button_time[i] = 0; do_button_short(i); } } } void level_check(void) { uint8_t i; uint8_t poti_lvl; for (i = 0; i < 4; i++) { poti_lvl = map(analogRead(poti_pin_by_number(i)), 15, 1023, 0, 255); if (poti_lvl < poti_level[i] - POTI_DEAD_ZONE || poti_lvl > poti_level[i] + POTI_DEAD_ZONE) { poti_level[i] = poti_lvl; #ifdef DEBUG Serial.print(F("Poti[")); Serial.print(i + 1, DEC); Serial.print(F("]: ")); Serial.println(poti_level[i]); #endif do_level(i); } } } void show_led(void) { analogWrite(LED_PIN, LED_PLAY_BRIGHTNESS); } void dmx_auto_colorchanger(void) { // DMX auto Fade //if (light_state != NORMAL) // return; for (uint8_t s = 0; s < MAX_DMX_SPOTS; s++) { uint8_t diff_counter = 0; if (spot[s].steps == 0) { for (uint8_t i = 0; i < spot[s].dmx_type; i++) { if (spot[s].diff[i] != 0.0) { diff_counter++; spot[s].diff[i] = 0.0; } } if (diff_counter != 0) { // start hold timer if (light_state != NORMAL) continue; int8_t start = MIN_DMX_HOLD_TIME + MIN_DMX_HOLD_TIME * poti_level[1] / 255.0 * 4; spot[s].steps = random(start, start + ((MAX_DMX_HOLD_TIME - start) * poti_level[DMX_HOLD_TIME] / 255.0)) * 1000 / DMX_COLOR_CHANGER_SCHED; #ifdef DEBUG Serial.print(F("Spot ")); Serial.print(s, DEC); Serial.print(F(" holding for ")); Serial.print(spot[s].steps * DMX_COLOR_CHANGER_SCHED / 1000, DEC); Serial.println(F(" seconds")); #endif } else { // new random values if (light_state != NORMAL) continue; RGBConverter color_converter; uint8_t rgb[3]; uint8_t start = MIN_DMX_FADE_TIME + MIN_DMX_FADE_TIME * poti_level[2] / 255.0 * 4; color_converter.hsvToRgb(random(0, 1024) / 1024.0, 1.0, 1.0, rgb); if (spot[s].dmx_type > 3) set_rgbw(&spot[s], random(start, start + ((MAX_DMX_FADE_TIME - start) * poti_level[DMX_FADE_TIME] / 255.0)), rgb, random(0, MAX_RAND_WHITE_LEVEL)); else set_rgb(&spot[s], random(start, start + ((MAX_DMX_FADE_TIME - start) * poti_level[DMX_FADE_TIME] / 255.0)), rgb); #ifdef DEBUG Serial.print(F("Spot ")); Serial.print(s, DEC); Serial.print(F(" changing color to R[")); Serial.print(spot[s].values[0], 2); Serial.print(F("] G[")); Serial.print(spot[s].values[1], 2); Serial.print(F("] B[")); Serial.print(spot[s].values[2], 2); if (spot[s].dmx_type > 3) { Serial.print(F("] W[")); Serial.print(spot[s].values[3], 2); } Serial.print(F("] for ")); Serial.print(spot[s].steps * DMX_COLOR_CHANGER_SCHED / 1000, DEC); Serial.println(F(" seconds")); #endif } } else { for (uint8_t i = 0; i < spot[s].dmx_type; i++) { if (spot[s].diff[i] != 0.0) { spot[s].values[i] += spot[s].diff[i]; if (spot[s].values[i] > 255.0) spot[s].values[i] = 255.0; else if (spot[s].values[i] < 0) spot[s].values[i] = 0.0; DmxSimple.write(spot[s].address + i, uint8_t(spot[s].values[i] * poti_level[DMX_BRIGHTNESS] / 255.0 + 0.5)); } #ifdef DEBUG Serial.print(F("Spot ")); Serial.print(s, DEC); Serial.print(F(" color ")); Serial.print(i, DEC); Serial.print(F(" step ")); Serial.print(spot[s].steps, DEC); Serial.print(F(" diff ")); Serial.print(spot[s].diff[i], 4); Serial.print(F(" value ")); Serial.println(spot[s].values[i], 4); #endif } } spot[s].steps--; } // Fog machine if (fog_state == true) { DmxSimple.write(DMX_FOG_MACHINE_ADDRESS, poti_level[DMX_FOG_LEVEL]); } else { DmxSimple.write(DMX_FOG_MACHINE_ADDRESS, 0); } } void test_worker(void) { uint8_t start = MIN_DMX_FADE_TIME + MIN_DMX_FADE_TIME * poti_level[1] / 255.0 * 4; uint8_t fade_time = random(start, start + ((MAX_DMX_FADE_TIME - start) * poti_level[1] / 255.0)); Serial.print("start:"); Serial.println(start, DEC); Serial.print("MAX_DMX_FADE_TIME"); Serial.println(MAX_DMX_FADE_TIME, DEC); Serial.print("POTI1:"); Serial.println(poti_level[1], DEC); Serial.print("POTI1/255:"); Serial.println(poti_level[1] / 255.0, 3); Serial.print("Fade-Time:"); Serial.println(fade_time, DEC); } //-------------------------------------------------------------------------------- // HELPER FUNCTIONS //-------------------------------------------------------------------------------- void do_level(uint8_t p) { #ifdef DEBUG Serial.print(F("POTI ")); Serial.print(p); Serial.print(F(": ")); Serial.println(poti_level[p]); #endif switch (p) { case 0: break; case 1: break; case 2: break; case 3: #ifdef DEBUG Serial.print(F("Fog: ")); Serial.print(poti_level[DMX_FOG_LEVEL] / 255.0 * 100.0); Serial.println(F("%")); #endif break; } } void do_button_long(uint8_t b) { #ifdef DEBUG Serial.print(F("BUTTON ")); Serial.print(b); Serial.println(F(" LONG")); #endif switch (b) { case 0: break; case 1: break; case 2: break; case 3: break; } } void do_button_short(uint8_t b) { #ifdef DEBUG Serial.print(F("BUTTON ")); Serial.print(b); Serial.println(F(" SHORT")); #endif switch (b) { case 0: if (light_state == OFF || light_state == WHITE) { #ifdef DEBUG Serial.println(F("Light: NORMAL")); #endif light_state = NORMAL; } else { #ifdef DEBUG Serial.println(F("Light: OFF")); #endif light_state = OFF; } break; case 1: if (light_state == OFF || light_state == NORMAL) { #ifdef DEBUG Serial.println(F("Light: WHITE")); #endif light_state = WHITE; for (uint8_t s = 0; s < MAX_DMX_SPOTS; s++) { uint8_t rgb[3] = { 255, 255, 255 }; for (uint8_t i = 0; i < spot[s].dmx_type; i++) if (spot[s].dmx_type > 3) set_rgbw(&spot[s], ON_OFF_FADE_TIME, rgb, 255); else set_rgb(&spot[s], ON_OFF_FADE_TIME, rgb); } } else { #ifdef DEBUG Serial.println(F("Light: OFF")); #endif light_state = OFF; } break; case 2: break; case 3: fog_state = !fog_state; #ifdef DEBUG if (fog_state) Serial.println(F("Fog: ON")); else Serial.println(F("Fog: OFF")); #endif break; } if (light_state == OFF) { for (uint8_t s = 0; s < MAX_DMX_SPOTS; s++) { uint8_t rgb[3] = { 0, 0, 0 }; for (uint8_t i = 0; i < spot[s].dmx_type; i++) if (spot[s].dmx_type > 3) set_rgbw(&spot[s], ON_OFF_FADE_TIME, rgb, 0); else set_rgb(&spot[s], ON_OFF_FADE_TIME, rgb); } } } uint32_t button(byte button_nr) { byte button = button_pin_by_number(button_nr); if (digitalRead(button) == LOW) { if (!bitRead(button_state, button_nr)) { bitSet(button_state, button_nr); button_time[button_nr] = millis(); } else { if (button_time[button_nr] > millis()) return (0xffff - button_time[button_nr] + millis()); // overflow-protection else return (millis() - button_time[button_nr]); } } else { if (bitRead(button_state, button_nr)) { bitWrite(button_state, button_nr, 0); if (button_time[button_nr] > millis()) return (0xffff - button_time[button_nr] + millis()); // overflow-protection else return (millis() - button_time[button_nr]); } } return (0); } uint8_t button_pin_by_number(byte n) { switch (n) { case 0: return (BUTTON1_PIN); break; case 1: return (BUTTON2_PIN); break; case 2: return (BUTTON3_PIN); break; case 3: return (BUTTON4_PIN); break; } return (0); } uint8_t poti_pin_by_number(byte n) { switch (n) { case 0: return (POTI1_PIN); break; case 1: return (POTI2_PIN); break; case 2: return (POTI3_PIN); break; case 3: return (POTI4_PIN); break; } return (0); } void set_rgbw(dmx_spot* spot, uint8_t fade, uint8_t* rgb, uint8_t w) { spot->steps = fade * 1000 / DMX_COLOR_CHANGER_SCHED; spot->diff[3] = (w - spot->values[3]) / spot->steps; set_rgb(spot, fade, rgb); } void set_rgb(dmx_spot* spot, uint8_t fade, uint8_t* rgb) { spot->steps = fade * 1000 / DMX_COLOR_CHANGER_SCHED; for (uint8_t i = 0; i < 3; i++) spot->diff[i] = (rgb[i] - spot->values[i]) / spot->steps; } //-------------------------------------------------------------------------------- // SYSTEM //-------------------------------------------------------------------------------- void setup() { #ifdef DEBUG Serial.begin(9600); #endif #ifdef __AVR_ATmega32U4__ for (uint8_t i = 0; i <= 10; i++) { if (!Serial) delay(100); else break; } #endif #ifdef DEBUG Serial.println(F("")); #endif mySoftwareSerial.begin(9600); randomSeed(analogRead(A6)); // setup poti pins pinMode(POTI1_PIN, INPUT_PULLUP); pinMode(POTI2_PIN, INPUT_PULLUP); pinMode(POTI3_PIN, INPUT_PULLUP); pinMode(POTI4_PIN, INPUT_PULLUP); // setup button pins pinMode(BUTTON1_PIN, INPUT_PULLUP); pinMode(BUTTON2_PIN, INPUT_PULLUP); pinMode(BUTTON3_PIN, INPUT_PULLUP); pinMode(BUTTON4_PIN, INPUT_PULLUP); // setup mp3 busy pin pinMode(BUSY_PIN, INPUT_PULLUP); // setup LED pin pinMode(LED_PIN, OUTPUT); // setup DMX DmxSimple.usePin(DMX_PIN); DmxSimple.maxChannel(DMX_MAX_CHANNEL); // DMX setup spot[0].address = 1; spot[1].address = 5; /* spot[2].address = 10; spot[3].address = 14; spot[4].address = 17; spot[5].address = 20; */ // setup audio card #ifdef DEBUG Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)")); #endif for (uint8_t n = 0; n < 3; n++) { if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3. #ifdef DEBUG Serial.print(F("Unable use DFPlayer: ")); Serial.println(n, DEC); #endif for (uint8_t i = 0; i < 3; i++) { analogWrite(LED_PIN, LED_PLAY_BRIGHTNESS); delay(100); analogWrite(LED_PIN, LED_PLAY_BRIGHTNESS); delay(100); } } else { #ifdef DEBUG Serial.println(F("DFPlayer Mini online.")); #endif myDFPlayer.setTimeOut(500); //Set serial communictaion time out 500ms break; } } myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD); // init schedular sched.addJob(button_check, SENSOR_SCHED); sched.addJob(level_check, LEVEL_CHECK_SCHED); sched.addJob(dmx_auto_colorchanger, DMX_COLOR_CHANGER_SCHED); sched.addJob(show_led, LED_SCHED); //sched.addJob(test_worker, 1000); #ifdef DEBUG Serial.println(F("")); #endif } void loop() { sched.scheduler(); }