// maximum delay between each relay
const int MAX_DELAY = 3000;
// number of animation routines
// (see "myAnimate" sub routine at the end of the code)
const int ALL_ANIMATIONS = 8;
// Pin for button speed switch
const int SPEED_PIN = 7;
// Pin for animation type
const int ANIMATION_PIN = 6;
// Pin tied to relay1
const uint8_t RELAY1 = A0;
// Pin tied to relay2
const uint8_t RELAY2 = A1;
// Pin tied to relay3
const uint8_t RELAY3 = A2;
// Pin tied to relay4
const uint8_t RELAY4 = A3;
// To make code more legible
const bool ON = true;
const bool OFF = false;
// Initial delay between relays
unsigned long delayMs = 500;
// variable to hold arduino LED value so we can flash it
bool LEDOnOff = false;
// the default starting animation
int animation = ALL_ANIMATIONS;
// variable to ensure we don't duplicate the last two animations when using ALL_ANIMATIONS
int lastAnimation[2];
// ------------------------------------------------------------
void setup() {
// Tell SPEED_PIN to test for itself and ground
pinMode(SPEED_PIN, INPUT_PULLUP);
// Tell ANIMATION_PIN to test for itself and ground
pinMode(ANIMATION_PIN, INPUT_PULLUP);
// Tell the Arduino LED that it will be used
pinMode(LED_BUILTIN, OUTPUT);
// Tell the analog pins that they'll be used
pinMode(RELAY1, OUTPUT);
pinMode(RELAY2, OUTPUT);
pinMode(RELAY3, OUTPUT);
pinMode(RELAY4, OUTPUT);
// Turn off all the relays
digitalWrite(RELAY1, HIGH);
digitalWrite(RELAY2, HIGH);
digitalWrite(RELAY3, HIGH);
digitalWrite(RELAY4, HIGH);
// Tell the Serial Monitor to communicate over USB to the PC
Serial.begin(115200);
}
// ------------------------------------------------------------
void loop() {
// put your main code here, to run repeatedly:
myAnimate(animation);
}
// ------------------------------------------------------------
// Toggle a relay on or off.
// myToggle (RELAY1 - 4, ON or OFF).
void myToggle (int myRelay, bool on_off){
// myRelay = RELAY1 - RELAY4
// on_off = ON (true) or OFF (false)
// debug print some info on the relay's state
Serial.print ("Pin "); Serial.print (myRelay);
// iif(on_off, " On", " Off") equivalent
// Serial.println (on_off ? (" On") : (" Off"));
if (on_off) {
// Turn on relay
digitalWrite(myRelay, HIGH);
// debug print On
Serial.println (" On");
} else {
// Turn off relay
digitalWrite(myRelay, LOW);
// debug print "Off"
Serial.println (" Off");
}
// Toggle Arduino LED between on / off (~ means bitwise Not)
LEDOnOff = ~LEDOnOff;
digitalWrite(LED_BUILTIN, LEDOnOff);
}
// ------------------------------------------------------------
//Checks for any button presses.
void check_input_pins () {
// Read the value of speed and animation pins
// 0 = it's pressed
// 1 = not pressed
int speed_switch = digitalRead(SPEED_PIN);
int animation_switch = digitalRead(ANIMATION_PIN);
// if speed button pressed
if (speed_switch == 0) {
delayMs += 250;
// if delay is at MAX_DELAY milliseconds, reset to 250 ms
if (delayMs > MAX_DELAY) {delayMs = 250;}
// debug print some data
Serial.print ("Speed switch pressed. Delay: ");
Serial.println (delayMs);
// wait for speed button to be released
while (speed_switch == 0){
// this delay necessary for humans to release switch
// otherwise gets buggy and changes speed too quickly
delay(250);
speed_switch = digitalRead(SPEED_PIN);
}
}
// if animation button pressed
if (animation_switch == 0) {
animation += 1;
// if we've run out of animations, start over
if (animation > ALL_ANIMATIONS) {animation = 1;}
// debug print some data
Serial.print ("Animation switch pressed. Change to animation ");
(animation == ALL_ANIMATIONS) ? Serial.println (" ALL") : Serial.println(animation);
// wait for animation button to be released
while (animation_switch == 0){
// this delay necessary for humans to release switch
// otherwise gets buggy and changes speed too quickly
delay(250);
animation_switch = digitalRead(ANIMATION_PIN);
}
}
// switches released or not pressed
}
// ------------------------------------------------------------
// Delays while polling for button presses.
// Use 0 for global delayMs value, or provide a millisecond delay duration.
void myDelay (int override_delay) {
// override_delay argument:
// 0 = use global delayMs
// [n] = use [n] millisecond delay for this call
// variables to time the start of the delay and end of the delay
unsigned long startMs;
unsigned long currentMs;
int relay_delay_ms = override_delay;
// if called with 0 parameter, use global delayMS for delay period
if (relay_delay_ms == 0) {relay_delay_ms = delayMs;}
// Get the current timer
currentMs = millis();
// Set the start timer to current timer
startMs = currentMs;
// Wait for delayMs to elapse
Serial.print ("Waiting "); Serial.println (relay_delay_ms);
while (currentMs - startMs < relay_delay_ms) {
currentMs = millis();
// check to see if the button to change delay period is being pressed
check_input_pins();
}
}
// ------------------------------------------------------------
// Delays for argDelayMs but not more than global delayMs.
void myFinalDelay(int argDelayMs) {
if (delayMs < argDelayMs) {
myDelay(argDelayMs);
} else {
myDelay(delayMs);
}
}
// ------------------------------------------------------------
// Animates by switching relays on and off.
// Usage: myAnimate (1 - ALL_ANIMATIONS).
void myAnimate (int do_animation) {
if (do_animation != ALL_ANIMATIONS ){Serial.print ("------ Running animation "); Serial.print(do_animation); Serial.println("------");}
switch (do_animation) {
case 1: // Straight through, only one on at a time with Delay in between.
myToggle(RELAY1, ON);
myDelay(0);
myToggle(RELAY2, ON);
myToggle(RELAY1, OFF);
myDelay(0);
myToggle(RELAY3, ON);
myToggle(RELAY2, OFF);
myDelay(0);
myToggle(RELAY4, ON);
myToggle(RELAY3, OFF);
myDelay(0);
myToggle(RELAY4, OFF);
myFinalDelay(1000);
break;
case 2: // Small trail behind ball with two on at a time
myToggle(RELAY1, ON);
myDelay(0);
myToggle(RELAY2, ON);
myDelay(175);
myToggle(RELAY1, OFF);
myDelay(0);
myToggle(RELAY3, ON);
myDelay(175);
myToggle(RELAY2, OFF);
myDelay(0);
myToggle(RELAY4, ON);
myDelay(175);
myToggle(RELAY3, OFF);
myDelay(0);
myToggle(RELAY4, OFF);
myFinalDelay(1000);
break;
case 3: // Long trail, fast ball, short pause then basket
myToggle(RELAY1, ON);
myDelay(250);
myToggle(RELAY2, ON);
myDelay(250);
myToggle(RELAY3, ON);
myToggle(RELAY1, OFF);
myDelay(250);
myToggle(RELAY2, OFF);
myDelay(250);
myToggle(RELAY4, ON);
myDelay(250);
myToggle(RELAY3, OFF);
myDelay(250);
myToggle(RELAY4, OFF);
myFinalDelay(1000);
break;
case 4: // Long trail, fast ball, trail catches up then flash then basket (star trek warp)
myToggle(RELAY1, ON);
myDelay(250);
myToggle(RELAY2, ON);
myDelay(250);
myToggle(RELAY3, ON);
myDelay(250);
myToggle(RELAY1, OFF);
myDelay(250);
myToggle(RELAY2, OFF);
myDelay(250);
myToggle(RELAY3, OFF);
myDelay(250);
myToggle(RELAY3, ON);
myDelay(250);
myToggle(RELAY3, OFF);
myDelay(250);
myToggle(RELAY3, ON);
myDelay(0);
myToggle(RELAY3, OFF);
myToggle(RELAY4, ON);
myDelay(0);
myToggle(RELAY4, OFF);
myFinalDelay(1000);
break;
case 5: // Santa holds ball for three seconds, then shoots animation 1
myToggle(RELAY1, ON);
myDelay(2000 - delayMs);
myAnimate(1);
break;
case 6: // Santa misses by ball bouncing back, then waits 2 seconds before shooting again.
myToggle(RELAY1, ON);
myDelay(0);
myToggle(RELAY1, OFF);
myToggle(RELAY2, ON);
myDelay(0);
myToggle(RELAY2, OFF);
myToggle(RELAY3, ON);
myDelay(0);
myToggle(RELAY3, OFF);
myDelay(250);
myToggle(RELAY3, ON);
myDelay(250);
myToggle(RELAY3, OFF);
myDelay(250);
myToggle(RELAY3, ON);
myDelay(250);
myToggle(RELAY3, OFF);
myDelay(250);
myToggle(RELAY3, ON);
myDelay(250);
myToggle(RELAY3, OFF);
myDelay(250);
myToggle(RELAY3, ON);
myDelay(250);
myToggle(RELAY3, OFF);
myDelay(250);
myToggle(RELAY3, ON);
myDelay(0);
myToggle(RELAY3, OFF);
myToggle(RELAY2, ON);
myDelay(0);
myToggle(RELAY2, OFF);
myFinalDelay(2000 - delayMs);
break;
case 7: // Santa misses, by under shooting the basket. Waits 1 second before shooting again. No trail behind ball.
myToggle(RELAY1, ON);
myDelay(0);
myToggle(RELAY2, ON);
myToggle(RELAY1, OFF);
myDelay(0);
myToggle(RELAY4, ON);
myToggle(RELAY2, OFF);
myDelay(0);
myToggle(RELAY4, OFF);
myFinalDelay(1000);
break;
case ALL_ANIMATIONS:
// Randomly pick an animation between 1 and ALL_ANIMATIONS.
// Arduino "random" does 1 less than maximum, i.e. (1, 4) = 1 or 2 or 3
// so we'll never get ALL_ANIMATIONS as a value from "random(1, ALL_ANIMATIONS)",
// and never get stuck here recursively.
int random_animation = lastAnimation[0];
while ((random_animation == lastAnimation[0]) || (random_animation == lastAnimation[1])) {
random_animation = random(1, ALL_ANIMATIONS);
}
// save this animation as lastAnimation
lastAnimation[1] = lastAnimation[0];
lastAnimation[0] = random_animation;
myAnimate(random_animation);
break;
}
// debug print some data
if (do_animation != ALL_ANIMATIONS ){Serial.print ("------ Exiting animation "); Serial.print(do_animation); Serial.println("------");}
}