/***************************************************
Holiday Show Control - v1.1 10/09/21
by Jeff W. Parisse - 09/09/2021
jparisse@me.com
TeensyDMX library (c) 2019-2020 Shawn Silverman
This program acts as a show controller for holiday show projects. It runs on a Teesy 3.2 and controls servos via
an Adafruit I2C servo controller, addressable LEDs via a Adafruit M4 Express processor board, sound via an Adafruit
16Mb Sound F/C Controller and DMX via a CTC-DRA-10-R2 Arduino Shield.
Processor: Teensy 3.2
PIR Sensor: 5V In, 3.3V Logic HIGH on Detect
DMX Shield: Generic using TeensyDMX Library
NEO Processor; Adafruit Itsy Bitsy M4 Express, LOW to trigger
Sound F/X Board: LOW to trigger
Servo Driver: I2C Address A1 (despite hardware A0 selected)
AC Relays: HIGH to turn ON
Pin Outs:
Pin 0 RX Serial In
Pin 1 TX Serial Out
Pin 2 DMX DOUT
Pin 3 PIR DIN
Pin 4 AUX DIN
Pin 5 NEO DOUT
Pin 6 AC1 DOUT
Pin 7 AC2 DOUT
Pin 8 SFX0 DOUT
Pin 9 SFX1 DOUT
Pin 10 SFX2 DOUT
Pin 11 SFX3 DOUT
Pin 12 SFX4 DOUT
Pin 13 LED DOUT
Pin 14 SFX5 DOUT
Pin 15 SFX6 DOUT
Pin 16 SFX7 DOUT
Pin 17 SFX8 DOUT
Pin 18 SDA I2C
Pin 19 SCL I2C
Pin 22 VOL+ DOUT (v1.1 not used)
Pin 23 VOL- DOUT (v1.1 not used)
****************************************************/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <TeensyDMX.h>
#define dmxPin 2
#define pirPin 3
#define auxPin 4
#define neoPin 5
#define ac1Pin 6
#define ac2Pin 7
#define sfx0Pin 8
#define sfx1Pin 9
#define sfx2Pin 10
#define sfx3Pin 11
#define sfx4Pin 12
#define ledPin 13
#define sfx5Pin 14
#define sfx6Pin 15
#define sfx7Pin 16
#define sfx8Pin 17
#define volupPin 22
#define voldnPin 23
#define servoFreq 50 // ~50 Hz servo updates
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
namespace teensydmx = ::qindesign::teensydmx;
teensydmx::Sender dmxTx{Serial1};
// DMX Variables
uint8_t data[7] = {0, 128, 128, 128, 0, 0, 0}; // Default: 50% White OFF
// Servo Variables
uint8_t servo1 = 0;
uint8_t servo2 = 1;
uint8_t servo3 = 2;
uint8_t servo4 = 3;
uint8_t servo5 = 4;
uint8_t servo6 = 5;
uint8_t servo7 = 6;
uint8_t servo8 = 7;
uint8_t servo9 = 8;
uint8_t servo10 = 9;
uint8_t servo11 = 10;
uint8_t servo12 = 11;
uint8_t servo13 = 12;
uint8_t servo14 = 13;
uint8_t servo15 = 14;
uint8_t servo16 = 15;
// Servo Timer
boolean servoDirection = LOW;
uint16_t servoMin = 167; // 900ms This is the 'minimum' pulse length count (out of 4096)
uint16_t servoCenter = 279; // 900ms This is the 'minimum' pulse length count (out of 4096)
uint16_t servoMax = 390; // 2100ms This is the 'maximum' pulse length count (out of 4096)
unsigned long servoInterval = 500; // Default
unsigned long previousServoMillis = 0;
// Moon Timer
boolean moonState = LOW;
uint16_t moonMin = 167; // 900ms This is the 'minimum' pulse length count (out of 4096)
uint16_t moonMax = 390; // 2100ms This is the 'maximum' pulse length count (out of 4096)
unsigned long moonInterval = 10; // Default Servo Delay
unsigned long previousMoonMillis = 0;
// Show Timer
boolean showState = LOW;
int showMin = 120000; // 120,000 ms = 00:02:00 (two minutes)
int showMax = 180000; // 180,000 ms = 00:03:00 (three minutes)
unsigned long showInterval = 120000; // 120,000 ms = 00:02:00
unsigned long previousShowMillis = 0;
// Delay Timer
boolean delayState = HIGH;
int delayMin = 30000; // 30,000 ms = 00:00:30 (thirty seconds)
int delayMax = 60000; // 60,000 ms = 00:01:00 (one minute)
unsigned long delayInterval = 15000; // 15,000 ms = 00:00:15
unsigned long previousDelayMillis = 0;
int songPick = 6; // Start with Song #6
int dmxPick = 1; // Default
int intervalPick = 2; // Default
int danceStep = 250; // Default
void setup() {
// Serial.begin(115200);
pwm.begin();
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(servoFreq); // Analog servos run at ~50 Hz updates
pinMode(pirPin, INPUT);
pinMode(auxPin, INPUT_PULLUP);
pinMode(dmxPin, OUTPUT);
pinMode(neoPin, OUTPUT);
pinMode(ac1Pin, OUTPUT);
pinMode(ac2Pin, OUTPUT);
pinMode(ledPin, OUTPUT);
pinMode(sfx0Pin, OUTPUT);
pinMode(sfx1Pin, OUTPUT);
pinMode(sfx2Pin, OUTPUT);
pinMode(sfx3Pin, OUTPUT);
pinMode(sfx4Pin, OUTPUT);
pinMode(sfx5Pin, OUTPUT);
pinMode(sfx6Pin, OUTPUT);
pinMode(sfx7Pin, OUTPUT);
pinMode(sfx8Pin, OUTPUT);
pinMode(volupPin, OUTPUT);
pinMode(voldnPin, OUTPUT);
// Turn everything OFF
pwm.setPWM(servo9, 0, moonMin);
digitalWrite(dmxPin, HIGH);
for (int x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
dmxTx.begin();
digitalWrite(neoPin, LOW);
digitalWrite(ac1Pin, LOW);
digitalWrite(ac2Pin, LOW);
digitalWrite(ledPin, LOW);
soundOff();
delay(4000); // Wait 4 seconds for PIR Module to go LOW
digitalWrite(sfx0Pin, LOW); // Ambient Sound ON
}
void loop() {
unsigned long currentMillis = millis(); // Check Time
boolean pirState = digitalRead(pirPin); // Check PIR Trigger
if (pirState == HIGH) { // If PIR triggered, change showState to HIGH
showState = HIGH;
digitalWrite(ledPin, HIGH);
}
else {
digitalWrite(ledPin, LOW);
}
if (showState == HIGH && delayState == HIGH) {
digitalWrite(sfx0Pin, HIGH); // Ambient Sound OFF
digitalWrite(sfx1Pin, LOW); // Play Paper Moon
digitalWrite(ac1Pin, HIGH); // Black Lights ON
if (moonState == LOW) {
for (uint16_t moonPos = moonMin; moonPos <= moonMax; ++moonPos) {
pwm.setPWM(servo9, 0, moonPos); // Test Servo
delay(10);
}
// wait for song to end
delay(5000); //test
digitalWrite(ac1Pin, LOW); // Black Lights ON
for (uint16_t moonPos = moonMax; moonPos <= moonMin; --moonPos) {
pwm.setPWM(servo9, 0, moonPos); // Test Servo
delay(10);
}
moonState = HIGH;
digitalWrite(sfx1Pin, HIGH); // Paper Moon OFF
}
// Dance Show Starts Here
digitalWrite(ac2Pin, HIGH); // Mirror Ball ON
digitalWrite(neoPin, HIGH); // Neopixels ON
playSong(); // Start Music
if (currentMillis - previousServoMillis >= servoInterval) {
previousServoMillis = currentMillis;
playDmx();
for (int x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
if (servoDirection == LOW) {
uint16_t pulseLengthLow1 = random(servoMin, servoCenter);
uint16_t pulseLengthLow2 = random(servoMin, servoCenter);
pwm.setPWM(servo1, 0, pulseLengthLow1);
pwm.setPWM(servo2, 0, pulseLengthLow2);
pwm.setPWM(servo9, 0, pulseLengthLow2); // Test Servo
servoInterval = getDanceStep();
servoDirection = HIGH;
}
else {
uint16_t pulseLengthHigh1 = random(servoCenter, servoMax);
uint16_t pulseLengthHigh2 = random(servoCenter, servoMax);
pwm.setPWM(servo1, 0, pulseLengthHigh1);
pwm.setPWM(servo2, 0, pulseLengthHigh2);
pwm.setPWM(servo9, 0, pulseLengthHigh2); // Test Servo
servoInterval = getDanceStep();
servoDirection = LOW;
}
}
// Check for End of Show
if (currentMillis - previousShowMillis >= showInterval) {
previousShowMillis = currentMillis;
previousDelayMillis = currentMillis;
showState = LOW; // Change STATE to Delay
delayState = LOW;
moonState = LOW;
pwm.setPWM(servo1, 0, servoCenter);
pwm.setPWM(servo2, 0, servoCenter);
pwm.setPWM(servo9, 0, servoCenter); // Test Servo
digitalWrite(neoPin, LOW); // NeoPixels Off
digitalWrite(ac2Pin, LOW); // AC 2 OFF
soundOff(); // Turn off sound
digitalWrite(sfx0Pin, LOW); // Sound #0 ON - Scene 0
++songPick; // Advance Song List by 1
if (songPick >= 9) { // If >8, Rollover Song List
songPick = 1;
}
showInterval = random(showMin, showMax); // Pick new show interval.
delayInterval = random(delayMin, delayMax); // Pick new delay interval.
}
}
if (currentMillis - previousDelayMillis >= delayInterval) {
previousDelayMillis = currentMillis;
delayState = HIGH;
}
}
// Functions
void soundOff() {
digitalWrite(sfx0Pin, HIGH);
digitalWrite(sfx1Pin, HIGH);
digitalWrite(sfx2Pin, HIGH);
digitalWrite(sfx3Pin, HIGH);
digitalWrite(sfx4Pin, HIGH);
digitalWrite(sfx5Pin, HIGH);
digitalWrite(sfx6Pin, HIGH);
digitalWrite(sfx7Pin, HIGH);
digitalWrite(sfx8Pin, HIGH);
digitalWrite(volupPin, HIGH);
digitalWrite(voldnPin, HIGH);
}
void playSong() {
switch (songPick) {
case 1:
digitalWrite(sfx1Pin, LOW);
break;
case 2:
digitalWrite(sfx2Pin, LOW);
break;
case 3:
digitalWrite(sfx3Pin, LOW);
break;
case 4:
digitalWrite(sfx4Pin, LOW);
break;
case 5:
digitalWrite(sfx5Pin, LOW);
break;
case 6:
digitalWrite(sfx6Pin, LOW);
break;
case 7:
digitalWrite(sfx7Pin, LOW);
break;
case 8:
digitalWrite(sfx8Pin, LOW);
break;
}
}
void playDmx() {
dmxPick = random(1, 11);
switch (dmxPick) {
case 1:
data[7] = {255, 255, 0, 0, 0, 0, 0}; // Red
break;
case 2:
data[7] = {255, 0, 255, 0, 0, 0, 0}; // Green
break;
case 3:
data[7] = {255, 0, 0, 255, 0, 0, 0}; // Blue
break;
case 4:
data[7] = {255, 255, 255, 255, 0, 0, 0}; // White
break;
case 5:
data[7] = {255, 255, 255, 0, 0, 0, 0}; // Yellow
break;
case 6:
data[7] = {255, 255, 0, 255, 0, 0, 0}; // Purple
break;
case 7:
data[7] = {255, 0, 255, 255, 0, 0, 0}; // Cyan
break;
case 8:
uint8_t data[7] = {255, 255, 255, 255, 0, 0, 0};
break;
case 9:
uint8_t data[7] = {255, 255, 255, 255, 0, 0, 0};
break;
case 10:
uint8_t data[7] = {255, 255, 255, 255, 0, 0, 0};
break;
}
// return data[7];
}
int getDanceStep() {
intervalPick = random(1, 5);
switch (intervalPick) {
case 1:
danceStep = 125;
break;
case 2:
danceStep = 250;
break;
case 3:
danceStep = 375;
break;
case 4:
danceStep = 500;
break;
}
return danceStep;
}