/*
RiTCh Lightshow
Simple Arduino based MP3 / DMX player which can be triggered by buttons .
( c ) 2024 by H . Wirtz < wirtz @ parasitstudio . de >
*/
/*
TODO :
-
*/
# include <SoftwareSerial.h>
# include <DFRobotDFPlayerMini.h>
# include "DmxSimple.h"
# include <EEPROM.h>
# include "looper.h"
# include <RGBConverter.h>
# 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 6
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 ( " <setup begin> " ) ) ;
# 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 ; // Stairville LED Par56 MKII RGBA 10mm SI https://images.thomann.de/pics/atg/atgdata/document/manual/274646_c_274644_274646_375069_v3_de_online.pdf
spot [ 1 ] . address = 5 ; // Stairville LED Par56 MKII RGBA 10mm SI
DmxSimple . write ( 9 , 0 ) ; // Init PAR64, channel: 9, DIP 1: on, DIP 4: on, DIP 8: off
spot [ 2 ] . address = 10 ; // Stairville PAR 64 https://images.thomann.de/pics/atg/atgdata/document/manual/c_193245_v2_r3_de_online.pdf
DmxSimple . write ( 15 , 41 ) ; // Init LED Bar for 3-segment-mode, channel: 15
spot [ 3 ] . address = 17 ; // Stairville LED BAR RGB 252 - Segment 1 https://images.thomann.de/pics/prod/255346_manual.pdf
spot [ 4 ] . address = 20 ; // Stairville LED BAR RGB 252 - Segment 2
spot [ 5 ] . address = 23 ; // Stairville LED BAR RGB 252 - Segment 3
// 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 ( " <setup end> " ) ) ;
# endif
}
void loop ( ) {
sched . scheduler ( ) ;
}