/***************************************************
Holiday Show Control - v1.1 10/15/21
by Jeff W. Parisse - 09/09/2021
jparisse@me.com
TeensyDMX library (c) 2019-2020 Shawn Silverman
Adafruit Servo Driver Library Written by Limor Fried/Ladyada for Adafruit Industries.
Thanks to Shawn and Paul S. for their help on the PJRC Forums.
This program acts as a show controller for holiday show projects. It runs on a Teesy 3.2 and controls servos via
a I2C servo controller, addressable LEDs via an external processor, sound via an 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
boolean dmxToggle = HIGH;
uint8_t data[7] = {255, 0, 0, 0, 0, 0, 0}; // Default: OFF
uint8_t dmxPick = 1; // Default
uint8_t x = 0; // For Loop Counter
// 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 (Blocking)
boolean moonState = LOW;
uint16_t moonUp = 167; // 900ms This is the 'minimum' pulse length count (out of 4096)
uint16_t moonDown = 390; // 2100ms This is the 'maximum' pulse length count (out of 4096)
unsigned long moonSongInterval = 30000; // Length of Song #1 - Paper Moon
// 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; // Default: 120,000 ms = 00:02:00
unsigned long previousShowMillis = 0;
// Delay Timer (Blocking)
boolean delayState = LOW;
int delayMin = 15000; // 15,000 ms = 00:00:15 (fifteen seconds)
int delayMax = 60000; // 60,000 ms = 00:01:00 (one minute)
int randomDelay = 0; // Default
// Show Default Variables
int songPick = 1; // Start with Song #1
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, moonDown); // Moon Servo
digitalWrite(dmxPin, HIGH); // Set DMX TX Pin to HIGH
for (x = 1; x <= 23; x = x + 7) { // Set DMX Lights to OFF
dmxTx.set(x, data, 7);
}
dmxTx.begin(); // Start DMX
digitalWrite(neoPin, LOW); // LEDs OFF
digitalWrite(ac1Pin, LOW); // Black Light OFF
digitalWrite(ac2Pin, LOW); // Mirrored Ball & Spotlight OFF (Not Used)
digitalWrite(ledPin, LOW); // Trigger Indicator OFF
soundOff(); // All sounds OFF
delay(3000); // Wait 3 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
digitalWrite(ledPin, HIGH);
showState = HIGH;
}
else {
digitalWrite(ledPin, LOW);
}
if (delayState == HIGH) {
delay(randomDelay);
delayState = LOW;
}
if (showState == HIGH) {
digitalWrite(sfx0Pin, HIGH); // Ambient Sound OFF
if (moonState == LOW) { // Check if Moon Show has run
digitalWrite(ac1Pin, HIGH); // Black Lights ON
digitalWrite(sfx1Pin, LOW); // Play Paper Moon
for (uint16_t moonPos = moonDown; moonPos >= moonUp; --moonPos) { // Moon UP
pwm.setPWM(servo9, 0, moonPos);
delay(10);
}
delay(moonSongInterval); // Wait for Song
for (uint16_t moonPos = moonUp; moonPos <= moonDown; ++moonPos) { // Moon DOWN
pwm.setPWM(servo9, 0, moonPos);
delay(10);
}
digitalWrite(ac1Pin, LOW); // Black Lights OFF
soundOff(); // All sound OFF
moonState = HIGH; // Moon Show has run
}
digitalWrite(ac2Pin, HIGH); // Mirror Ball ON
digitalWrite(neoPin, HIGH); // Neopixels ON
playSong(); // Start Music
// Dance Show Starts Here
if (currentMillis - previousServoMillis >= servoInterval) { //
previousServoMillis = currentMillis;
playServo();
if (dmxToggle == HIGH); {
playDmx();
}
dmxToggle = !dmxToggle;
}
if (currentMillis - previousShowMillis >= showInterval) { // Check for End of Show
previousShowMillis = currentMillis;
showState = LOW;
moonState = LOW;
delayState = HIGH;
pwm.setPWM(servo1, 0, servoCenter); // Center Servo1
pwm.setPWM(servo2, 0, servoCenter); // Center Servo2
pwm.setPWM(servo9, 0, moonDown); // Moon DOWN
digitalWrite(neoPin, LOW); // Turn NeoPixels Off
digitalWrite(ac2Pin, LOW); // AC 2 OFF - Mirrored Ball & Pin Spot
soundOff(); // Turn Sound OFF
dmxOff(); // Turn DMX OFF
digitalWrite(sfx0Pin, LOW); // Sound #0 ON - Scene 0
++songPick; // Advance Song List by 1
if (songPick >= 8) { // If >7, Rollover Song List
songPick = 1;
}
showInterval = random(showMin, showMax); // Pick new show interval.
randomDelay = random(delayMin, delayMax); // Pick new delay interval.
}
}
}
// 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 dmxOff() {
data[0] = 255;
data[1] = 0;
data[2] = 0;
data[3] = 0;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
}
void playSong() {
switch (songPick) {
case 1:
digitalWrite(sfx2Pin, LOW);
break;
case 2:
digitalWrite(sfx3Pin, LOW);
break;
case 3:
digitalWrite(sfx4Pin, LOW);
break;
case 4:
digitalWrite(sfx5Pin, LOW);
break;
case 5:
digitalWrite(sfx6Pin, LOW);
break;
case 6:
digitalWrite(sfx7Pin, LOW);
break;
case 7:
digitalWrite(sfx8Pin, LOW);
break;
}
}
void playServo() {
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);
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);
servoInterval = getDanceStep();
servoDirection = LOW;
}
}
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;
}
void playDmx() {
dmxPick = random(1, 11);
switch (dmxPick) {
case 1: // Red
data[0] = 255;
data[1] = 255;
data[2] = 0;
data[3] = 0;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
case 2: // Green
data[0] = 255;
data[1] = 0;
data[2] = 255;
data[3] = 0;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
case 3: // Blue
data[0] = 255;
data[1] = 0;
data[2] = 0;
data[3] = 255;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
case 4: // White
data[0] = 255;
data[1] = 255;
data[2] = 255;
data[3] = 255;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
case 5: // Yellow
data[0] = 255;
data[1] = 255;
data[2] = 255;
data[3] = 0;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
case 6: // Purple
data[0] = 255;
data[1] = 255;
data[2] = 0;
data[3] = 255;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
case 7: // Cyan
data[0] = 255;
data[1] = 0;
data[2] = 255;
data[3] = 255;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
case 8: // White
data[0] = 255;
data[1] = 255;
data[2] = 255;
data[3] = 255;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
case 9: // Yellow
data[0] = 255;
data[1] = 255;
data[2] = 255;
data[3] = 0;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
case 10: // Red
data[0] = 255;
data[1] = 255;
data[2] = 0;
data[3] = 0;
data[4] = 0;
data[5] = 0;
data[6] = 0;
for (x = 1; x <= 23; x = x + 7) {
dmxTx.set(x, data, 7);
}
break;
}
}