|
@@ -0,0 +1,1241 @@
|
|
|
+/*
|
|
|
+ Cider Drag Races
|
|
|
+
|
|
|
+ A project to let two racers compete in drinking a glass of cider,
|
|
|
+ complete with start button, weight activated drink coasters,
|
|
|
+ a drag-strip "christmas tree", and buzzer.
|
|
|
+
|
|
|
+ Along with a few easter eggs.
|
|
|
+
|
|
|
+ Pin Assignments per Default Sketch:
|
|
|
+ Digital:
|
|
|
+ 2 - Left Player Display
|
|
|
+ 3 - Right Player Display
|
|
|
+ 4 - unused (RX for left display)
|
|
|
+ 5 - unused (RX for right display)
|
|
|
+ 13 - Left Ready Button
|
|
|
+ 7 - Left Pressure Simulator
|
|
|
+ 8 - Right Ready Button
|
|
|
+ 9 - Buzzer (+)
|
|
|
+ 10 - Right Pressure Simularor
|
|
|
+ 11 - Left Player Light Tree (6 lights) (If WS2812B)
|
|
|
+ 12 - Right Player Light Tree (6 lights)(If WS2812B)
|
|
|
+ 30-39 - Colored LEDs
|
|
|
+
|
|
|
+ Analog:
|
|
|
+ 0 - Right Player Pressure Sensor
|
|
|
+ 1 - Left Player Pressure Sensor
|
|
|
+ 7 - Unused; Seeds RNG.
|
|
|
+
|
|
|
+
|
|
|
+ Fred Damstra
|
|
|
+ fred.damstra@gmail.com
|
|
|
+
|
|
|
+ 2016-09-29 - Initial Draft
|
|
|
+
|
|
|
+ Licensed under GPLv3: https://www.gnu.org/licenses/gpl.html
|
|
|
+*/
|
|
|
+#include <SoftwareSerial.h>
|
|
|
+#include "Adafruit_NeoPixel.h"
|
|
|
+#include "WS2812_Definitions.h"
|
|
|
+#include <eRCaGuy_ButtonReader.h>
|
|
|
+
|
|
|
+/**********
|
|
|
+ CONFIGURATION SECTION
|
|
|
+*/
|
|
|
+
|
|
|
+/* Buzzer Setup:
|
|
|
+ Wire ground to common ground, and positive to a PWM
|
|
|
+ pin. In this example, pin 9.
|
|
|
+
|
|
|
+ Lots of help for this example at
|
|
|
+ https://learn.sparkfun.com/tutorials/sik-experiment-guide-for-arduino---v32/experiment-11-using-a-piezo-buzzer
|
|
|
+*/
|
|
|
+const int debug = 0;
|
|
|
+
|
|
|
+const int buzzerPin = 9;
|
|
|
+const unsigned long songFrequency = 90000; // How frequently the song may play in seconds
|
|
|
+const float songProbability = 0.0; // Probability that the song will play during that frequency
|
|
|
+
|
|
|
+/* randomPin should be an unused analog pin for seeding the RNG */
|
|
|
+const int randomPin = 5;
|
|
|
+
|
|
|
+/* WS2812 Christmas Tree Setup:
|
|
|
+ Two strips of 6 LEDs connected to 5V and Ground, plus the signal pin
|
|
|
+*/
|
|
|
+const int ledPinRight = 12;
|
|
|
+const int ledPinLeft = 11;
|
|
|
+const int brightness = 255;
|
|
|
+
|
|
|
+/* Digital (old school) Christmas Tree Setup:
|
|
|
+ Two strips of 6 LEDs connected to 5V and Ground, plus the signal pin
|
|
|
+*/
|
|
|
+//const int ledPinsLeft[] = {30, 32, 34, 36, 38};
|
|
|
+const int ledPinsLeft[] = {38, 36, 34, 32, 30};
|
|
|
+//const int ledPinsRight[] = {31, 33, 35, 37, 39};
|
|
|
+const int ledPinsRight[] = {39, 37, 35, 33, 31};
|
|
|
+
|
|
|
+const unsigned long prettyLEDFrequency = 30; // How frequently something pretty happens on the LEDs
|
|
|
+const float prettyLEDProbability = 0.75; // Probability that the song will play during that frequency
|
|
|
+
|
|
|
+/* Christmas Tree */
|
|
|
+const unsigned long startDelay = 1000; /* How long to wait between hitting the start ubtton and starting */
|
|
|
+const unsigned long maxDelay = 5000; /* In non dragrace mode, how long might we wait *after* a guaranteed startDelay? */
|
|
|
+const unsigned long lightDelay = 500; /* How long to wait between lights during the countdown */
|
|
|
+const unsigned long topColor = PURPLE; /* Top LED is constant to show power */
|
|
|
+
|
|
|
+/* 7-Segment Display Setup
|
|
|
+ Two 7 OpenSegment displays from SparkFun in serial mode.
|
|
|
+*/
|
|
|
+const int leftDisplayTX = 2;
|
|
|
+const int rightDisplayTX = 3;
|
|
|
+const int leftDisplayRX = 4; /* Unused */
|
|
|
+const int rightDisplayRX = 5; /* Unused */
|
|
|
+
|
|
|
+/* Force Sensitive Resistor Switches (Analog inputs) */
|
|
|
+const int leftFSR = A7;
|
|
|
+const int rightFSR = A0;
|
|
|
+const int leftFSRThreshold = 10; /* > this number is closed */
|
|
|
+const int rightFSRThreshold = 10;
|
|
|
+const int leftFSRSimPin = 7; /* For when the FSRs aren't there */
|
|
|
+const int rightFSRSimPin = 10; /* For when the FSRs aren't there */
|
|
|
+
|
|
|
+/* Start Button */
|
|
|
+const int leftReadyButtonPin = 13;
|
|
|
+const int rightReadyButtonPin = 8;
|
|
|
+
|
|
|
+/* Difficulty */
|
|
|
+const int difficultyLeftPin = 46;
|
|
|
+const int difficultyRightPin = 47;
|
|
|
+
|
|
|
+/* Loop delays
|
|
|
+ n.b. It occurs to me that unusual values for these variables /may/ upset
|
|
|
+ the math on looping. Probably not, but if you run into problems, return
|
|
|
+ these to original values of 100 and 25 respectively
|
|
|
+*/
|
|
|
+const int mainDelay = 100; /* How long to sleep during the main loop */
|
|
|
+const int countdownDelay = 25; /* How long to sleep during the countdown loop */
|
|
|
+
|
|
|
+/* Special Functions */
|
|
|
+const int modePin = 51; /* Red */
|
|
|
+const int buzzPin = 52; /* Green */
|
|
|
+const int clearPin = 53; /* White */
|
|
|
+
|
|
|
+
|
|
|
+/***********
|
|
|
+ Application Constants
|
|
|
+*/
|
|
|
+const int LED_COUNT = 6;
|
|
|
+
|
|
|
+const unsigned int buzzerFrequency = 70;
|
|
|
+const int buzzerDuration = 1800;
|
|
|
+const unsigned int beepFrequency = 3700;
|
|
|
+const int beepDuration = 100;
|
|
|
+const int beepMultiplier = 3; /* long beep is this x as long a short beep */
|
|
|
+
|
|
|
+const unsigned long renegDelay = 500;
|
|
|
+const unsigned long debounceDelay = 250;
|
|
|
+
|
|
|
+/***********
|
|
|
+ Application Variables
|
|
|
+*/
|
|
|
+unsigned long lastSongCheck = 0;
|
|
|
+Adafruit_NeoPixel rightTree = Adafruit_NeoPixel(LED_COUNT, ledPinRight, NEO_GRB + NEO_KHZ800);
|
|
|
+Adafruit_NeoPixel leftTree = Adafruit_NeoPixel(LED_COUNT, ledPinLeft, NEO_GRB + NEO_KHZ800);
|
|
|
+
|
|
|
+SoftwareSerial leftDisplay(leftDisplayRX, leftDisplayTX);
|
|
|
+SoftwareSerial rightDisplay(rightDisplayRX, rightDisplayTX);
|
|
|
+
|
|
|
+eRCaGuy_ButtonReader leftReadyButton = eRCaGuy_ButtonReader(leftReadyButtonPin);
|
|
|
+eRCaGuy_ButtonReader rightReadyButton = eRCaGuy_ButtonReader(rightReadyButtonPin);
|
|
|
+eRCaGuy_ButtonReader leftFSRButton = eRCaGuy_ButtonReader(leftFSRSimPin);
|
|
|
+eRCaGuy_ButtonReader rightFSRButton = eRCaGuy_ButtonReader(rightFSRSimPin);
|
|
|
+eRCaGuy_ButtonReader clearToggle = eRCaGuy_ButtonReader(clearPin);
|
|
|
+
|
|
|
+
|
|
|
+/* Buzzer: Some constants for playing a song: */
|
|
|
+const int songLength = 18;
|
|
|
+char notes[] = "cdfda ag cdfdg gf ";
|
|
|
+int beats[] = {1, 1, 1, 1, 1, 1, 4, 4, 2, 1, 1, 1, 1, 1, 1, 4, 4, 2};
|
|
|
+const int tempo = 118;
|
|
|
+
|
|
|
+int songLength2 = 26;
|
|
|
+char notes2[] = "eeeeeeegcde fffffeeeeddedg";
|
|
|
+int beats2[] = { 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2};
|
|
|
+const int tempo2 = 130;
|
|
|
+
|
|
|
+/* LEDs */
|
|
|
+unsigned long lastPrettyLEDCheck = 0;
|
|
|
+
|
|
|
+/* 7 Segment */
|
|
|
+long rightClock = 0;
|
|
|
+long leftClock = 0;
|
|
|
+
|
|
|
+#define APOSTROPHE 5
|
|
|
+#define COLON 4
|
|
|
+#define DECIMAL4 3
|
|
|
+#define DECIMAL3 2
|
|
|
+#define DECIMAL2 1
|
|
|
+#define DECIMAL1 0
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/***********
|
|
|
+ Code
|
|
|
+*/
|
|
|
+void setup() {
|
|
|
+ Serial.begin(9600); // Serial used for logging
|
|
|
+
|
|
|
+ /* Buzzer Setup */
|
|
|
+ pinMode(buzzerPin, OUTPUT);
|
|
|
+ randomSeed(analogRead(randomPin));
|
|
|
+
|
|
|
+
|
|
|
+ /* WS2812 LED Setup */
|
|
|
+ rightTree.begin();
|
|
|
+ leftTree.begin();
|
|
|
+ /* Digital LED Setup */
|
|
|
+ for(int i = 0; i < 5; i++) {
|
|
|
+ pinMode(ledPinsLeft[i], OUTPUT);
|
|
|
+ digitalWrite(ledPinsLeft[i], LOW);
|
|
|
+ pinMode(ledPinsRight[i], OUTPUT);
|
|
|
+ digitalWrite(ledPinsRight[i], LOW);
|
|
|
+ }
|
|
|
+ /* clearLEDs(); */
|
|
|
+ clearLEDs();
|
|
|
+
|
|
|
+ /* 7 Segment Displays */
|
|
|
+ leftDisplay.listen();
|
|
|
+ leftDisplay.begin(9600);
|
|
|
+ leftDisplay.write('v');
|
|
|
+ leftDisplay.write(0x7A);
|
|
|
+ leftDisplay.write((byte) brightness);
|
|
|
+ rightDisplay.listen();
|
|
|
+ rightDisplay.begin(9600);
|
|
|
+ rightDisplay.write('v');
|
|
|
+ rightDisplay.write(0x7A);
|
|
|
+ rightDisplay.write((byte) brightness);
|
|
|
+ longbeep();
|
|
|
+ printClocks(1223,1996);
|
|
|
+
|
|
|
+ /* Initialize our buttons */
|
|
|
+ pinMode(leftReadyButtonPin, INPUT_PULLUP);
|
|
|
+ pinMode(rightReadyButtonPin, INPUT_PULLUP);
|
|
|
+ pinMode(leftFSRSimPin, INPUT_PULLUP);
|
|
|
+ pinMode(rightFSRSimPin, INPUT_PULLUP);
|
|
|
+ pinMode(leftFSR, INPUT);
|
|
|
+ pinMode(rightFSR, INPUT);
|
|
|
+
|
|
|
+ /* And our switches */
|
|
|
+ pinMode(modePin, INPUT_PULLUP); /* Red */
|
|
|
+ pinMode(clearPin, INPUT_PULLUP); /* Green */
|
|
|
+ pinMode(buzzPin, INPUT_PULLUP); /* White */
|
|
|
+
|
|
|
+ /* And our sliders */
|
|
|
+ pinMode(difficultyLeftPin, INPUT_PULLUP); /* HIGH is normal difficulty */
|
|
|
+ pinMode(difficultyRightPin, INPUT_PULLUP);
|
|
|
+
|
|
|
+ /* Demo the Christmas Tree */
|
|
|
+ setLEDs(BLACK, BLACK, BLACK, BLACK, YELLOW);
|
|
|
+ longbeep(); delay(400);
|
|
|
+ setLEDs(BLACK, BLACK, BLACK, YELLOW, BLACK);
|
|
|
+ longbeep(); delay(400);
|
|
|
+ setLEDs(BLACK, BLACK, YELLOW, BLACK, BLACK);
|
|
|
+ longbeep(); delay(400);
|
|
|
+ setLEDs(BLACK, GREEN, BLACK, BLACK, BLACK);
|
|
|
+ longbeep(); delay(400);
|
|
|
+ setLEDs(RED, BLACK, BLACK, BLACK, BLACK);
|
|
|
+ longbeep();
|
|
|
+
|
|
|
+ /* Arbitrary delay before updating the 7-segments again */
|
|
|
+ delay(1000);
|
|
|
+
|
|
|
+ /* 7 Segment Display Continued */
|
|
|
+ if(random(0, 1000) < 100) {
|
|
|
+ leftClock=531;
|
|
|
+ rightClock=8008;
|
|
|
+ printClocks(leftClock, rightClock);
|
|
|
+ } else {
|
|
|
+ leftClock=1223;
|
|
|
+ rightClock=2016;
|
|
|
+ printClocks(leftClock,rightClock);
|
|
|
+ }
|
|
|
+ longbeep();
|
|
|
+
|
|
|
+ /* Switches */
|
|
|
+ pinMode(leftFSR, INPUT);
|
|
|
+ pinMode(rightFSR, INPUT);
|
|
|
+
|
|
|
+ /* start button */
|
|
|
+ //pinMode(startButtonPin, INPUT_PULLUP);
|
|
|
+ Serial.println("Setup complete.");
|
|
|
+}
|
|
|
+
|
|
|
+/* MAIN LOOP */
|
|
|
+void loop() {
|
|
|
+ int8_t clearButtonAction;
|
|
|
+ boolean clearButtonState;
|
|
|
+ // put your main code here, to run repeatedly:
|
|
|
+ easterEggSongCheck(); // Sometimes we play a song
|
|
|
+ prettyLEDCheck();
|
|
|
+
|
|
|
+ clearToggle.readButton(&clearButtonAction, &clearButtonState);
|
|
|
+
|
|
|
+ if(clearButtonAction == 1) {
|
|
|
+ Serial.println("clear action called");
|
|
|
+ if(random(0, 1000) < 100) {
|
|
|
+ leftClock=531;
|
|
|
+ rightClock=8008;
|
|
|
+ printClocks(leftClock, rightClock);
|
|
|
+ } else {
|
|
|
+ leftClock=0;
|
|
|
+ rightClock=0;
|
|
|
+ printClocks(leftClock,rightClock);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ printClocks(leftClock, rightClock);
|
|
|
+
|
|
|
+ shouldwebuzz();
|
|
|
+
|
|
|
+ /* If either coaster is open (no glass), continue on */
|
|
|
+ if (leftSwitchOpen() || rightSwitchOpen()) {
|
|
|
+ clearLEDs();
|
|
|
+ if(!leftSwitchOpen()) {
|
|
|
+ digitalWrite(ledPinsLeft[0], HIGH);
|
|
|
+ } else if(!rightSwitchOpen()) {
|
|
|
+ digitalWrite(ledPinsRight[0], HIGH);
|
|
|
+ }
|
|
|
+ delay(mainDelay);
|
|
|
+ return; /* Can't continue in main loop */
|
|
|
+ } else {
|
|
|
+ /* Both are closed, race can begin */
|
|
|
+ ledsToRed();
|
|
|
+ if(millis() % 5000 == 0) {
|
|
|
+ Serial.println("Waiting for start button to be pressed.");
|
|
|
+ }
|
|
|
+ if (startButtonPressed()) {
|
|
|
+ countdown();
|
|
|
+ /* We're back from a race */
|
|
|
+ /* Reset easter eggs */
|
|
|
+ lastSongCheck = lastPrettyLEDCheck = millis();
|
|
|
+ startButtonPressed(); /* Clear out "stuck" state input */
|
|
|
+ //return; /* no continue in main loop */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Rest before looping again */
|
|
|
+ delay(mainDelay);
|
|
|
+}
|
|
|
+
|
|
|
+void countdown(void) {
|
|
|
+ unsigned long randomAdditionalDelay;
|
|
|
+ int mode; /* 1 is drag race, 0 is randomness */
|
|
|
+ int cstep = -1; /* Which step of the countdown are we on? */
|
|
|
+ rightClock = leftClock = 0;
|
|
|
+
|
|
|
+ if(digitalRead(modePin) == HIGH) {
|
|
|
+ /* Drag race mode */
|
|
|
+ mode = 1;
|
|
|
+ } else {
|
|
|
+ /* Random delay mode */
|
|
|
+ setLEDs(RED, BLACK, BLACK, BLACK, YELLOW);
|
|
|
+ randomAdditionalDelay = random(maxDelay);
|
|
|
+ mode = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(mode) {
|
|
|
+ Serial.print("Start button pressed. Beginning xmas tree in ");
|
|
|
+ Serial.print(startDelay);
|
|
|
+ Serial.println("ms");
|
|
|
+ } else {
|
|
|
+ Serial.print("Start button pressed. Skipping xmas tree. Beginning in ");
|
|
|
+ Serial.print(startDelay + randomAdditionalDelay);
|
|
|
+ Serial.println("ms.");
|
|
|
+ }
|
|
|
+ printClocks(leftClock, rightClock);
|
|
|
+ startsong();
|
|
|
+ delay(startDelay);
|
|
|
+
|
|
|
+ unsigned long start = millis();
|
|
|
+ while (1) {
|
|
|
+
|
|
|
+ shouldwebuzz();
|
|
|
+
|
|
|
+ if (leftSwitchOpen_debounce(1) || rightSwitchOpen_debounce(1)) {
|
|
|
+ /* FALSE START! */
|
|
|
+ if (leftSwitchOpen_debounce(1)) {
|
|
|
+ Serial.println("FALSE START: Player Left");
|
|
|
+ buzz_nonblock();
|
|
|
+ leftClock = 9999;
|
|
|
+ rightClock = 0;
|
|
|
+ printClocks(leftClock, rightClock);
|
|
|
+ for(int i = 0; i < 3; i++) {
|
|
|
+ leftDisplay.listen();
|
|
|
+ leftDisplay.write(0x7A);
|
|
|
+ leftDisplay.write((byte) 0);
|
|
|
+ delay(150);
|
|
|
+ leftDisplay.listen();
|
|
|
+ leftDisplay.write(0x7A);
|
|
|
+ leftDisplay.write((byte) brightness);
|
|
|
+ delay(150);
|
|
|
+ }
|
|
|
+ delay(buzzerDuration - 900); /* Rest for the remaining buzz */
|
|
|
+ leftSwitchOpen_debounce(1);
|
|
|
+ rightSwitchOpen_debounce(1); /* Get another reading just to clear things out */
|
|
|
+ return;
|
|
|
+ } else if (rightSwitchOpen_debounce(1)) {
|
|
|
+ Serial.println("FALSE START: Player Right");
|
|
|
+ buzz_nonblock();
|
|
|
+ rightClock = 9999;
|
|
|
+ leftClock = 0;
|
|
|
+ printClocks(leftClock, rightClock);
|
|
|
+ for(int i = 0; i < 3; i++) {
|
|
|
+ rightDisplay.listen();
|
|
|
+ rightDisplay.write(0x7A);
|
|
|
+ rightDisplay.write((byte) 0);
|
|
|
+ delay(150);
|
|
|
+ rightDisplay.listen();
|
|
|
+ rightDisplay.write(0x7A);
|
|
|
+ rightDisplay.write((byte) 255);
|
|
|
+ delay(150);
|
|
|
+ }
|
|
|
+ delay(buzzerDuration - 900); /* Rest for the remaining buzz */
|
|
|
+ leftSwitchOpen_debounce(1);
|
|
|
+ rightSwitchOpen_debounce(1); /* Get another reading just to clear things out */
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ /* WTF? Timing issue */
|
|
|
+ Serial.println("WARNING: False start detected, but corrected?!");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* No false start */
|
|
|
+
|
|
|
+ if(mode) {
|
|
|
+ /* drag race! */
|
|
|
+ switch ( (millis() - start) / lightDelay ) {
|
|
|
+ case 0:
|
|
|
+ if (cstep != 0) {
|
|
|
+ Serial.println("Countdown ... 3");
|
|
|
+ setLEDs(RED, BLACK, BLACK, BLACK, YELLOW);
|
|
|
+ beep();
|
|
|
+ cstep = 0;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ case 1:
|
|
|
+ if (cstep != 1) {
|
|
|
+ Serial.println("Countdown ... 2");
|
|
|
+ setLEDs(RED, BLACK, BLACK, YELLOW, BLACK);
|
|
|
+ beep();
|
|
|
+ cstep = 1;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ case 2:
|
|
|
+ if (cstep != 2) {
|
|
|
+ Serial.println("Countdown ... 1");
|
|
|
+ setLEDs(RED, BLACK, YELLOW, BLACK, BLACK);
|
|
|
+ beep();
|
|
|
+ cstep = 2;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ case 3:
|
|
|
+ Serial.println("Countdown ... GO!!!!!!!!");
|
|
|
+ setLEDs(BLACK, GREEN, BLACK, BLACK, BLACK);
|
|
|
+ longbeep();
|
|
|
+ race_loop();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* Silent start */
|
|
|
+ if(millis() > (start + startDelay + randomAdditionalDelay)) {
|
|
|
+ Serial.println("Random delay met... GO!!!!!!!!");
|
|
|
+ setLEDs(BLACK, GREEN, BLACK, BLACK, BLACK);
|
|
|
+ longbeep();
|
|
|
+ race_loop();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ delay(countdownDelay); /* Make the loop less aggressive */
|
|
|
+ }
|
|
|
+ Serial.println("Should never get here...");
|
|
|
+}
|
|
|
+
|
|
|
+void race_loop(void) {
|
|
|
+ /* Race is on! */
|
|
|
+ unsigned long start = millis();
|
|
|
+ Serial.print("Race is ON at ");
|
|
|
+ Serial.println(start);
|
|
|
+
|
|
|
+ boolean handicap_left = false;
|
|
|
+ boolean handicap_right = false;
|
|
|
+
|
|
|
+ unsigned long timerLeft, timerRight;
|
|
|
+ timerLeft = timerRight = 0;
|
|
|
+
|
|
|
+ /* determine handicap settings */
|
|
|
+ if(digitalRead(difficultyLeftPin) == 0) {
|
|
|
+ handicap_left = true;
|
|
|
+ }
|
|
|
+ if(digitalRead(difficultyRightPin) == 0) {
|
|
|
+ handicap_right = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Record whether the racer picked up his/her cup */
|
|
|
+ int leftStarted = 0;
|
|
|
+ int rightStarted = 0;
|
|
|
+ unsigned long loopstart = millis();
|
|
|
+ unsigned long lastloop = millis();
|
|
|
+ unsigned long leftRenegBounce = 0;
|
|
|
+ unsigned long rightRenegBounce = 0;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ loopstart = millis();
|
|
|
+ //Serial.print("Loop time with serial: ");
|
|
|
+ //Serial.println(loopstart - lastloop);
|
|
|
+ /* Check for winners */
|
|
|
+ boolean leftOpen, rightOpen;
|
|
|
+ leftOpen = rightOpen = false;
|
|
|
+
|
|
|
+ /* Choose which one to read first randomly to offset any potential bias. I
|
|
|
+ * find that the loop is generally less than 1ms, so this is probably unnecessary,
|
|
|
+ * but it doesn't hurt.
|
|
|
+ */
|
|
|
+ if(random(0, 2)) {
|
|
|
+// Serial.print("Left first. Time = ");
|
|
|
+// Serial.println(millis());
|
|
|
+ leftOpen = leftSwitchOpen();
|
|
|
+ rightOpen = rightSwitchOpen();
|
|
|
+ } else {
|
|
|
+// Serial.print("Right first. Time = ");
|
|
|
+// Serial.println(millis());
|
|
|
+ rightOpen = rightSwitchOpen();
|
|
|
+ leftOpen = leftSwitchOpen();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check for finishers */
|
|
|
+ if (!leftOpen && leftStarted && !timerLeft) {
|
|
|
+ /* Finished */
|
|
|
+ Serial.print("Left player finished at ");
|
|
|
+ Serial.println(millis() - start);
|
|
|
+ timerLeft = (millis() - start) / 10;
|
|
|
+ if(handicap_left) {
|
|
|
+ /* Handicapped player */
|
|
|
+ timerLeft = timerLeft - (timerLeft >> 2);
|
|
|
+ }
|
|
|
+ beep_nonblock();
|
|
|
+ } else if (leftOpen && !leftStarted) {
|
|
|
+ Serial.print("Left cup lifted at ");
|
|
|
+ Serial.println(millis() - start);
|
|
|
+ leftStarted = 1;
|
|
|
+ }
|
|
|
+ if (!rightOpen && rightStarted && !timerRight) {
|
|
|
+ /* Finished */
|
|
|
+ Serial.print("Right player finished at ");
|
|
|
+ Serial.println(millis() - start);
|
|
|
+ timerRight = (millis() - start) / 10;
|
|
|
+ if(handicap_right) {
|
|
|
+ /* Handicapped player */
|
|
|
+ timerRight = timerRight - (timerRight >> 2);
|
|
|
+ }
|
|
|
+ beep_nonblock();
|
|
|
+ } else if (rightOpen && !rightStarted) {
|
|
|
+ Serial.print("Right cup lifted at ");
|
|
|
+ Serial.println(millis() - start);
|
|
|
+ rightStarted = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check for renegers
|
|
|
+ * Note to cheaters: too hard a hit can make the opponent's cup bounce.
|
|
|
+ * However, we can't turn this off, because things bounce on removal.
|
|
|
+ */
|
|
|
+ if (leftSwitchOpen() && timerLeft) {
|
|
|
+ /* Bullshit! */
|
|
|
+ if(leftRenegBounce == 0) {
|
|
|
+ leftRenegBounce = millis();
|
|
|
+ } else if((millis() - leftRenegBounce) > renegDelay) {
|
|
|
+ Serial.println("Left player RENEGS! This is bullshit!");
|
|
|
+ timerLeft = 0;
|
|
|
+ leftRenegBounce = 0;
|
|
|
+ } else {
|
|
|
+ Serial.print("Left reneg on delay: ");
|
|
|
+ Serial.println(millis() - leftRenegBounce);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (rightSwitchOpen() && timerRight) {
|
|
|
+ /* Bullshit! */
|
|
|
+ if(rightRenegBounce == 0) {
|
|
|
+ rightRenegBounce = millis();
|
|
|
+ } else if((millis() - rightRenegBounce) > renegDelay) {
|
|
|
+ Serial.println("Right player RENEGS! This is bullshit!");
|
|
|
+ timerRight = 0;
|
|
|
+ rightRenegBounce = 0;
|
|
|
+ } else {
|
|
|
+ Serial.print("Right reneg on delay: ");
|
|
|
+ Serial.println(millis() - rightRenegBounce);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If still a winner and not a reneger, I think we have a winner */
|
|
|
+ if (timerLeft) {
|
|
|
+ leftClock = timerLeft;
|
|
|
+ } else {
|
|
|
+ leftClock = (millis() - start) / 10;
|
|
|
+ if(handicap_left) {
|
|
|
+ /* Handicapped player */
|
|
|
+ leftClock = leftClock - (leftClock >> 2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (timerRight) {
|
|
|
+ rightClock = timerRight;
|
|
|
+ } else {
|
|
|
+ rightClock = (millis() - start) / 10;
|
|
|
+ if(handicap_right) {
|
|
|
+ /* Handicapped player */
|
|
|
+ rightClock = rightClock - (rightClock >> 2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* Print the clocks */
|
|
|
+ printClocks(leftClock, rightClock);
|
|
|
+ if (timerRight && timerLeft) {
|
|
|
+ /* Race is finished */
|
|
|
+ ledsToRed();
|
|
|
+ if(timerRight < timerLeft) {
|
|
|
+ Serial.println("Right player WINS!");
|
|
|
+ for(int i = 0; i < 3; i++) {
|
|
|
+ rightDisplay.listen();
|
|
|
+ rightDisplay.write(0x7A);
|
|
|
+ rightDisplay.write((byte) 0);
|
|
|
+ delay(150);
|
|
|
+ beep();
|
|
|
+ rightDisplay.listen();
|
|
|
+ rightDisplay.write(0x7A);
|
|
|
+ rightDisplay.write((byte) 255);
|
|
|
+ delay(150);
|
|
|
+ }
|
|
|
+ } else if(timerLeft < timerRight) {
|
|
|
+ for(int i = 0; i < 3; i++) {
|
|
|
+ leftDisplay.listen();
|
|
|
+ leftDisplay.write(0x7A);
|
|
|
+ leftDisplay.write((byte) 0);
|
|
|
+ delay(150);
|
|
|
+ beep();
|
|
|
+ leftDisplay.listen();
|
|
|
+ leftDisplay.write(0x7A);
|
|
|
+ leftDisplay.write((byte) 255);
|
|
|
+ delay(150);
|
|
|
+ }
|
|
|
+ Serial.println("Left player WINS!");
|
|
|
+ } else {
|
|
|
+ Serial.println("TIE GAME!!!");
|
|
|
+ }
|
|
|
+ playWinner();
|
|
|
+ /* Wait until start buttons are no longer pressed */
|
|
|
+ while(startButtonPressed()) {
|
|
|
+ delay(100);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ lastloop = loopstart;
|
|
|
+ }
|
|
|
+ Serial.println("ERROR: Woah. Just Woah. How the fuck? 0x73184fcgukl");
|
|
|
+}
|
|
|
+
|
|
|
+/* Beep and buzz if those toggles are pressed */
|
|
|
+void shouldwebuzz() {
|
|
|
+ if(digitalRead(buzzPin) == LOW) {
|
|
|
+ buzz_nonblock();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Start button switch */
|
|
|
+int startButtonPressed() {
|
|
|
+ int8_t leftButtonAction, rightButtonAction;
|
|
|
+ boolean leftButtonState, rightButtonState;
|
|
|
+
|
|
|
+ leftReadyButton.readButton(&leftButtonAction, &leftButtonState);
|
|
|
+ rightReadyButton.readButton(&rightButtonAction, &rightButtonState);
|
|
|
+
|
|
|
+ if(leftButtonState == false && rightButtonState == false) {
|
|
|
+ Serial.println("Both ready buttons pressed.");
|
|
|
+ return 1;
|
|
|
+ } else {
|
|
|
+/* Serial.print("Left state: ");
|
|
|
+ Serial.println(leftButtonState);
|
|
|
+ Serial.print("Right state: ");
|
|
|
+ Serial.println(rightButtonState);
|
|
|
+*/
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* FSR Functions */
|
|
|
+int leftSwitchOpen() {
|
|
|
+ return leftSwitchOpen_debounce(0);
|
|
|
+}
|
|
|
+
|
|
|
+int leftSwitchOpen_debounce(int silent) {
|
|
|
+ /* Not a standard debounce method. We're going to keep the last 16 values
|
|
|
+ * and majority rules.
|
|
|
+ */
|
|
|
+ static int results[16] = {0,0,0,0,0,
|
|
|
+ 0,0,0,0,0,
|
|
|
+ 0,0,0,0,0,0};
|
|
|
+ static int pointer = 0;
|
|
|
+ results[pointer] = leftSwitchOpen(silent);
|
|
|
+ pointer = (pointer + 1) % 16;
|
|
|
+
|
|
|
+ int sum = 0;
|
|
|
+// Serial.print("Left array: [");
|
|
|
+ for(int i = 0; i < 16; i++) {
|
|
|
+ sum+=results[i];
|
|
|
+// Serial.print(results[i]);
|
|
|
+// Serial.print(",");
|
|
|
+ }
|
|
|
+ if(sum > 8) {
|
|
|
+// Serial.println("]; Returning 1");
|
|
|
+ return 1;
|
|
|
+ } else {
|
|
|
+// Serial.println("]; Returning 0");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int leftSwitchOpen(int silent) {
|
|
|
+ int8_t leftButtonAction;
|
|
|
+ boolean leftButtonState;
|
|
|
+ static int lastvalue = 0;
|
|
|
+
|
|
|
+/* Sim Mode:
|
|
|
+ leftFSRButton.readButton(&leftButtonAction, &leftButtonState);
|
|
|
+ if(leftButtonState == false) { */
|
|
|
+ /* Button is low, meaning it's pressed */
|
|
|
+/* return 0;
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+*/
|
|
|
+ int value = analogRead(leftFSR);
|
|
|
+ if( value > leftFSRThreshold) {
|
|
|
+ /* Pressed! */
|
|
|
+ if(lastvalue < leftFSRThreshold) {
|
|
|
+ Serial.print("Left pressed, pressure: ");
|
|
|
+ Serial.println(value);
|
|
|
+ }
|
|
|
+ lastvalue = value;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if(lastvalue > leftFSRThreshold) {
|
|
|
+ Serial.print("Left released, pressure: ");
|
|
|
+ Serial.println(value);
|
|
|
+ lastvalue = value;
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+int rightSwitchOpen() {
|
|
|
+ return rightSwitchOpen_debounce(0);
|
|
|
+}
|
|
|
+
|
|
|
+int rightSwitchOpen_debounce(int silent) {
|
|
|
+ /* Not a standard debounce method. We're going to keep the last 16 values
|
|
|
+ * and majority rules.
|
|
|
+ */
|
|
|
+ static int results[16] = {0,0,0,0,0,
|
|
|
+ 0,0,0,0,0,
|
|
|
+ 0,0,0,0,0,0};
|
|
|
+ static int pointer = 0;
|
|
|
+ results[pointer] = rightSwitchOpen(silent);
|
|
|
+ pointer = (pointer + 1) % 16;
|
|
|
+
|
|
|
+ int sum = 0;
|
|
|
+// Serial.print("Right array: [");
|
|
|
+ for(int i = 0; i < 16; i++) {
|
|
|
+ sum+=results[i];
|
|
|
+// Serial.print(results[i]);
|
|
|
+// Serial.print(",");
|
|
|
+ }
|
|
|
+ if(sum > 8) {
|
|
|
+// Serial.println("]; Returning 1");
|
|
|
+ return 1;
|
|
|
+ } else {
|
|
|
+// Serial.println("]; Returning 0");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int rightSwitchOpen(int silent) {
|
|
|
+ int8_t rightButtonAction;
|
|
|
+ boolean rightButtonState;
|
|
|
+ static int lastvalue = 0;
|
|
|
+
|
|
|
+/*
|
|
|
+ rightFSRButton.readButton(&rightButtonAction, &rightButtonState);
|
|
|
+ if(rightButtonState == false) { */
|
|
|
+ /*Button is low, meaning it's pressed */
|
|
|
+/* return 0;
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+*/
|
|
|
+ int value = analogRead(rightFSR);
|
|
|
+ if( value > rightFSRThreshold) {
|
|
|
+ /* Pressed! */
|
|
|
+ if(lastvalue < rightFSRThreshold) {
|
|
|
+ Serial.print("Right pressed, pressure: ");
|
|
|
+ Serial.println(value);
|
|
|
+ }
|
|
|
+ lastvalue = value;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if(lastvalue > rightFSRThreshold) {
|
|
|
+ Serial.print("Right released, pressure: ");
|
|
|
+ Serial.println(value);
|
|
|
+ lastvalue = value;
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+int switchOpen(int pin, int threshold, int silent) {
|
|
|
+ int fsrADC = analogRead(pin);
|
|
|
+#ifdef SIMULATION
|
|
|
+ fsrADC = random(0, 1000);
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (fsrADC < threshold) {
|
|
|
+ if (!silent) {
|
|
|
+ Serial.print(fsrADC);
|
|
|
+ Serial.println(" -- OPEN");
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ if (!silent) {
|
|
|
+ Serial.print(fsrADC);
|
|
|
+ Serial.println(" -- CLOSED");
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 7-Segment Functions */
|
|
|
+void printClocks(unsigned long leftValue, unsigned long rightValue) {
|
|
|
+ if (leftValue > 9999) {
|
|
|
+ leftValue = 9999;
|
|
|
+ }
|
|
|
+ if (rightValue > 9999) {
|
|
|
+ rightValue = 9999;
|
|
|
+ }
|
|
|
+// Serial.print("Clocks: ");
|
|
|
+ char tempstring[10];
|
|
|
+ snprintf(tempstring, sizeof(tempstring), "%04lu", leftValue);
|
|
|
+ leftDisplay.listen();
|
|
|
+ leftDisplay.write(tempstring);
|
|
|
+// Serial.print(tempstring);
|
|
|
+ snprintf(tempstring, sizeof(tempstring), "%04lu", rightValue);
|
|
|
+ rightDisplay.listen();
|
|
|
+ rightDisplay.write(tempstring);
|
|
|
+// Serial.print(" ");
|
|
|
+// Serial.println(tempstring);
|
|
|
+
|
|
|
+ /* TUrn on decimal point */
|
|
|
+ leftDisplay.listen();
|
|
|
+ leftDisplay.write(0x77); /* Decimal, colon, and apostrophe command */
|
|
|
+ leftDisplay.write(0b000010); /* Decimal 2 is second bit) */
|
|
|
+ rightDisplay.listen();
|
|
|
+ rightDisplay.write(0x77); /* Decimal, colon, and apostrophe command */
|
|
|
+ rightDisplay.write(0b000010); /* Decimal 2 is second bit) */
|
|
|
+}
|
|
|
+
|
|
|
+/* LED Functions */
|
|
|
+void ledsToRed() {
|
|
|
+ ws2812_ledsToRed();
|
|
|
+ digitalWrite(ledPinsLeft[0], HIGH);
|
|
|
+ digitalWrite(ledPinsRight[0], HIGH);
|
|
|
+ for(int i=1; i<5; i++) {
|
|
|
+ digitalWrite(ledPinsLeft[i], LOW);
|
|
|
+ digitalWrite(ledPinsRight[i], LOW);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Sets red light on.
|
|
|
+void ws2812_ledsToRed() {
|
|
|
+ clearLEDs();
|
|
|
+ rightTree.setPixelColor(0, RED);
|
|
|
+ leftTree.setPixelColor(0, RED);
|
|
|
+ rightTree.show();
|
|
|
+ leftTree.show();
|
|
|
+}
|
|
|
+
|
|
|
+/* Clears all LEDs (for WS2812, except the top one) */
|
|
|
+void clearLEDs() {
|
|
|
+ ws2812_clearLEDs();
|
|
|
+ for(int i = 0; i < 5; i++) {
|
|
|
+ digitalWrite(ledPinsLeft[i], LOW);
|
|
|
+ digitalWrite(ledPinsRight[i], LOW);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ws2812_clearLEDs()
|
|
|
+{
|
|
|
+ for (int i = 0; i < (LED_COUNT - 1); i++)
|
|
|
+ {
|
|
|
+ rightTree.setPixelColor(i, 0);
|
|
|
+ leftTree.setPixelColor(i, 0);
|
|
|
+ }
|
|
|
+ rightTree.setPixelColor(LED_COUNT - 1, topColor);
|
|
|
+ leftTree.setPixelColor(LED_COUNT - 1, topColor);
|
|
|
+ rightTree.show();
|
|
|
+ leftTree.show();
|
|
|
+}
|
|
|
+
|
|
|
+void setLEDs(unsigned long redlight,
|
|
|
+ unsigned long greenlight,
|
|
|
+ unsigned long yellow3,
|
|
|
+ unsigned long yellow2,
|
|
|
+ unsigned long yellow1) {
|
|
|
+ ws2812_setLEDs(redlight, greenlight, yellow3, yellow2, yellow1);
|
|
|
+
|
|
|
+ /* If black, turn it off, otherwise turn it on */
|
|
|
+ if(redlight == BLACK) {
|
|
|
+ digitalWrite(ledPinsLeft[0], LOW);
|
|
|
+ digitalWrite(ledPinsRight[0], LOW);
|
|
|
+ } else {
|
|
|
+ digitalWrite(ledPinsLeft[0], HIGH);
|
|
|
+ digitalWrite(ledPinsRight[0], HIGH);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If black, turn it off, otherwise turn it on */
|
|
|
+ if(greenlight == BLACK) {
|
|
|
+ digitalWrite(ledPinsLeft[1], LOW);
|
|
|
+ digitalWrite(ledPinsRight[1], LOW);
|
|
|
+ } else {
|
|
|
+ digitalWrite(ledPinsLeft[1], HIGH);
|
|
|
+ digitalWrite(ledPinsRight[1], HIGH);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If black, turn it off, otherwise turn it on */
|
|
|
+ if(yellow3 == BLACK) {
|
|
|
+ digitalWrite(ledPinsLeft[2], LOW);
|
|
|
+ digitalWrite(ledPinsRight[2], LOW);
|
|
|
+ } else {
|
|
|
+ digitalWrite(ledPinsLeft[2], HIGH);
|
|
|
+ digitalWrite(ledPinsRight[2], HIGH);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If black, turn it off, otherwise turn it on */
|
|
|
+ if(yellow2 == BLACK) {
|
|
|
+ digitalWrite(ledPinsLeft[3], LOW);
|
|
|
+ digitalWrite(ledPinsRight[3], LOW);
|
|
|
+ } else {
|
|
|
+ digitalWrite(ledPinsLeft[3], HIGH);
|
|
|
+ digitalWrite(ledPinsRight[3], HIGH);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If black, turn it off, otherwise turn it on */
|
|
|
+ if(yellow1 == BLACK) {
|
|
|
+ digitalWrite(ledPinsLeft[4], LOW);
|
|
|
+ digitalWrite(ledPinsRight[4], LOW);
|
|
|
+ } else {
|
|
|
+ digitalWrite(ledPinsLeft[4], HIGH);
|
|
|
+ digitalWrite(ledPinsRight[4], HIGH);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ws2812_setLEDs(unsigned long redlight,
|
|
|
+ unsigned long greenlight,
|
|
|
+ unsigned long yellow3,
|
|
|
+ unsigned long yellow2,
|
|
|
+ unsigned long yellow1) {
|
|
|
+ rightTree.setPixelColor(0, redlight);
|
|
|
+ rightTree.setPixelColor(1, greenlight);
|
|
|
+ rightTree.setPixelColor(2, yellow3);
|
|
|
+ rightTree.setPixelColor(3, yellow2);
|
|
|
+ rightTree.setPixelColor(4, yellow1);
|
|
|
+ rightTree.setPixelColor(LED_COUNT - 1, topColor);
|
|
|
+ leftTree.setPixelColor(0, redlight);
|
|
|
+ leftTree.setPixelColor(1, greenlight);
|
|
|
+ leftTree.setPixelColor(2, yellow3);
|
|
|
+ leftTree.setPixelColor(3, yellow2);
|
|
|
+ leftTree.setPixelColor(4, yellow1);
|
|
|
+ leftTree.setPixelColor(LED_COUNT - 1, topColor);
|
|
|
+ rightTree.show();
|
|
|
+ leftTree.show();
|
|
|
+}
|
|
|
+
|
|
|
+void prettyLEDCheck() {
|
|
|
+ /* Lets see if we should play a song */
|
|
|
+ if ( (millis() - lastPrettyLEDCheck) > (prettyLEDFrequency * 1000) ) {
|
|
|
+ lastPrettyLEDCheck = millis();
|
|
|
+ if ( (random(0, 1000) < long(prettyLEDProbability * 1000.0)) ) {
|
|
|
+ Serial.println("Pretty LED Approved");
|
|
|
+ prettyLED();
|
|
|
+ } else {
|
|
|
+ Serial.println("Pretty lED probability check declined.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void prettyLED() {
|
|
|
+ /* if using the ws2812's, you may want to turn this back on
|
|
|
+ ws2812_prettyLED();
|
|
|
+ */
|
|
|
+ if(random(0, 1000) < 500) {
|
|
|
+ prettyCircleLED();
|
|
|
+ prettyCircleLED();
|
|
|
+// prettyCircleLED();
|
|
|
+ } else {
|
|
|
+ prettyBounceLED();
|
|
|
+ prettyBounceLED();
|
|
|
+ prettyBounceLED();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void prettyCircleLED() {
|
|
|
+ clearLEDs();
|
|
|
+ for(int i = 4; i >= 0; i--) {
|
|
|
+ shouldwebuzz();
|
|
|
+ Serial.print("Pin: ");
|
|
|
+ Serial.println(i);
|
|
|
+ digitalWrite(ledPinsLeft[i], HIGH);
|
|
|
+ delay(100);
|
|
|
+ digitalWrite(ledPinsLeft[i], LOW);
|
|
|
+ }
|
|
|
+ for(int i = 0; i < 5; i++) {
|
|
|
+ shouldwebuzz();
|
|
|
+ Serial.print("Pin: ");
|
|
|
+ Serial.println(i);
|
|
|
+ digitalWrite(ledPinsRight[i], HIGH);
|
|
|
+ delay(100);
|
|
|
+ digitalWrite(ledPinsRight[i], LOW);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void prettyBounceLED() {
|
|
|
+ clearLEDs();
|
|
|
+ for(int i = 4; i >= 0; i--) {
|
|
|
+ shouldwebuzz();
|
|
|
+ digitalWrite(ledPinsLeft[i], HIGH);
|
|
|
+ digitalWrite(ledPinsRight[i], HIGH);
|
|
|
+ delay(100);
|
|
|
+ digitalWrite(ledPinsLeft[i], LOW);
|
|
|
+ digitalWrite(ledPinsRight[i], LOW);
|
|
|
+ }
|
|
|
+ for(int i = 1; i < 5; i++) {
|
|
|
+ shouldwebuzz();
|
|
|
+ digitalWrite(ledPinsLeft[i], HIGH);
|
|
|
+ digitalWrite(ledPinsRight[i], HIGH);
|
|
|
+ delay(100);
|
|
|
+ digitalWrite(ledPinsLeft[i], LOW);
|
|
|
+ digitalWrite(ledPinsRight[i], LOW);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void ws2812_prettyLED() {
|
|
|
+ /* Do something interesting */
|
|
|
+/* cylon(YELLOW, 500); */
|
|
|
+}
|
|
|
+
|
|
|
+// Implements a little larson "cylon" sanner.
|
|
|
+// This'll run one full cycle, down one way and back the other
|
|
|
+void ws2812_cylon(unsigned long color, unsigned long wait)
|
|
|
+{
|
|
|
+ Serial.println("Cylons are attacking.");
|
|
|
+ // weight determines how much lighter the outer "eye" colors are
|
|
|
+ const byte weight = 4;
|
|
|
+ // It'll be easier to decrement each of these colors individually
|
|
|
+ // so we'll split them out of the 24-bit color value
|
|
|
+ byte red = (color & 0xFF0000) >> 16;
|
|
|
+ byte green = (color & 0x00FF00) >> 8;
|
|
|
+ byte blue = (color & 0x0000FF);
|
|
|
+
|
|
|
+ // Start at closest LED, and move to the outside
|
|
|
+ for (int i = 0; i <= LED_COUNT - 1; i++)
|
|
|
+ {
|
|
|
+ clearLEDs();
|
|
|
+ rightTree.setPixelColor(i, red, green, blue); // Set the bright middle eye
|
|
|
+ leftTree.setPixelColor(i, red, green, blue); // Set the bright middle eye
|
|
|
+ // Now set two eyes to each side to get progressively dimmer
|
|
|
+ for (int j = 1; j < 3; j++)
|
|
|
+ {
|
|
|
+ if (i - j >= 0)
|
|
|
+ leftTree.setPixelColor(i - j, red / (weight * j), green / (weight * j), blue / (weight * j));
|
|
|
+ rightTree.setPixelColor(i - j, red / (weight * j), green / (weight * j), blue / (weight * j));
|
|
|
+ if (i - j <= LED_COUNT)
|
|
|
+ leftTree.setPixelColor(i + j, red / (weight * j), green / (weight * j), blue / (weight * j));
|
|
|
+ rightTree.setPixelColor(i + j, red / (weight * j), green / (weight * j), blue / (weight * j));
|
|
|
+ }
|
|
|
+ leftTree.show(); // Turn the LEDs on
|
|
|
+ rightTree.show(); // Turn the LEDs on
|
|
|
+ delay(wait); // Delay for visibility
|
|
|
+ }
|
|
|
+
|
|
|
+ // Now we go back to where we came. Do the same thing.
|
|
|
+ for (int i = LED_COUNT - 2; i >= 1; i--)
|
|
|
+ {
|
|
|
+ clearLEDs();
|
|
|
+ leftTree.setPixelColor(i, red, green, blue);
|
|
|
+ rightTree.setPixelColor(i, red, green, blue);
|
|
|
+ for (int j = 1; j < 3; j++)
|
|
|
+ {
|
|
|
+ if (i - j >= 0)
|
|
|
+ leftTree.setPixelColor(i - j, red / (weight * j), green / (weight * j), blue / (weight * j));
|
|
|
+ rightTree.setPixelColor(i - j, red / (weight * j), green / (weight * j), blue / (weight * j));
|
|
|
+ if (i - j <= LED_COUNT)
|
|
|
+ leftTree.setPixelColor(i + j, red / (weight * j), green / (weight * j), blue / (weight * j));
|
|
|
+ rightTree.setPixelColor(i + j, red / (weight * j), green / (weight * j), blue / (weight * j));
|
|
|
+ }
|
|
|
+
|
|
|
+ leftTree.show();
|
|
|
+ rightTree.show();
|
|
|
+ delay(wait);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Song Easter Egg */
|
|
|
+void easterEggSongCheck() {
|
|
|
+ /* Lets see if we should play a song */
|
|
|
+ if ( (millis() - lastSongCheck) > (songFrequency * 1000) ) {
|
|
|
+ lastSongCheck = millis();
|
|
|
+ if ( (random(0, 1000) < long(songProbability * 1000.0)) ) {
|
|
|
+ Serial.println("Song probability check approved. Never give up!");
|
|
|
+ playsong();
|
|
|
+ } else {
|
|
|
+ Serial.println("Song probability check declined.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int frequency(char note)
|
|
|
+{
|
|
|
+ // This function takes a note character (a-g), and returns the
|
|
|
+ // corresponding frequency in Hz for the tone() function.
|
|
|
+
|
|
|
+ int i;
|
|
|
+ const int numNotes = 8; // number of notes we're storing
|
|
|
+
|
|
|
+ // The following arrays hold the note characters and their
|
|
|
+ // corresponding frequencies. The last "C" note is uppercase
|
|
|
+ // to separate it from the first lowercase "c". If you want to
|
|
|
+ // add more notes, you'll need to use unique characters.
|
|
|
+
|
|
|
+ // For the "char" (character) type, we put single characters
|
|
|
+ // in single quotes.
|
|
|
+
|
|
|
+ char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
|
|
|
+ int frequencies[] = {262, 294, 330, 349, 392, 440, 494, 523};
|
|
|
+
|
|
|
+ // Now we'll search through the letters in the array, and if
|
|
|
+ // we find it, we'll return the frequency for that note.
|
|
|
+
|
|
|
+ for (i = 0; i < numNotes; i++) // Step through the notes
|
|
|
+ {
|
|
|
+ if (names[i] == note) // Is this the one?
|
|
|
+ {
|
|
|
+ return (frequencies[i]); // Yes! Return the frequency
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return (0); // We looked through everything and didn't find it,
|
|
|
+ // but we still need to return a value, so return 0.
|
|
|
+}
|
|
|
+
|
|
|
+void playWinner() {
|
|
|
+ /* TODO: Ideally, this is "Enjoy MonkeyBOX", but anything will do */
|
|
|
+ /* Also, ideally won't block */
|
|
|
+ if(random(0, 10) < 9) {
|
|
|
+ playsong();
|
|
|
+ } else {
|
|
|
+ playsong2();
|
|
|
+ }
|
|
|
+ Serial.println("Insert winner music here.");
|
|
|
+}
|
|
|
+
|
|
|
+void startsong() {
|
|
|
+ /* TODO: Make this something fancier */
|
|
|
+ beep();
|
|
|
+ delay(200);
|
|
|
+ beep();
|
|
|
+ delay(200);
|
|
|
+ beep();
|
|
|
+ delay(200);
|
|
|
+}
|
|
|
+
|
|
|
+void buzz() {
|
|
|
+ /* play a loud, annoying buzz */
|
|
|
+ Serial.println("*BUZZ*");
|
|
|
+ tone(buzzerPin, buzzerFrequency, buzzerDuration);
|
|
|
+ delay(buzzerDuration);
|
|
|
+}
|
|
|
+
|
|
|
+void buzz_nonblock() {
|
|
|
+ /* play a loud, annoying buzz */
|
|
|
+ Serial.println("*BUZZ*");
|
|
|
+ tone(buzzerPin, buzzerFrequency, buzzerDuration);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void beep() {
|
|
|
+ /* play a simple, pleasant beep */
|
|
|
+ Serial.println("*beep*");
|
|
|
+ tone(buzzerPin, beepFrequency, beepDuration);
|
|
|
+ delay(beepDuration);
|
|
|
+}
|
|
|
+
|
|
|
+void beep_nonblock() {
|
|
|
+ /* play a simple, pleasant beep, but don't wait for it to finish */
|
|
|
+ Serial.println("*beep* (but don't wait for it)");
|
|
|
+ tone(buzzerPin, beepFrequency, beepDuration);
|
|
|
+}
|
|
|
+
|
|
|
+void longbeep() {
|
|
|
+ /* play a simple, pleasant beep */
|
|
|
+ Serial.println("*beeeeeeeeeeeeeeeeeeeeeeeeeep*");
|
|
|
+ tone(buzzerPin, beepFrequency, beepDuration * beepMultiplier);
|
|
|
+ /* longbeep doesn't delay. Maybe that's inconsistent */
|
|
|
+ /* delay(beepDuration * beepMultiplier); */
|
|
|
+}
|
|
|
+
|
|
|
+void playsong() {
|
|
|
+ /* Plays a song.
|
|
|
+ Cut directly from the SparkFun tutorial at
|
|
|
+ https://learn.sparkfun.com/tutorials/sik-experiment-guide-for-arduino---v32/experiment-11-using-a-piezo-buzzer
|
|
|
+ */
|
|
|
+ int i, duration;
|
|
|
+
|
|
|
+ for (i = 0; i < songLength; i++) // step through the song arrays
|
|
|
+ {
|
|
|
+ duration = beats[i] * tempo; // length of note/rest in ms
|
|
|
+
|
|
|
+ if (notes[i] == ' ') // is this a rest?
|
|
|
+ {
|
|
|
+ delay(duration); // then pause for a moment
|
|
|
+ }
|
|
|
+ else // otherwise, play the note
|
|
|
+ {
|
|
|
+ tone(buzzerPin, frequency(notes[i]), duration);
|
|
|
+ delay(duration); // wait for tone to finish
|
|
|
+ }
|
|
|
+ delay(tempo / 10); // brief pause between notes
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void playsong2() {
|
|
|
+ /* Plays a song.
|
|
|
+ Cut directly from the SparkFun tutorial at
|
|
|
+ https://learn.sparkfun.com/tutorials/sik-experiment-guide-for-arduino---v32/experiment-11-using-a-piezo-buzzer
|
|
|
+ */
|
|
|
+ int i, duration;
|
|
|
+
|
|
|
+ for (i = 0; i < songLength2; i++) // step through the song arrays
|
|
|
+ {
|
|
|
+ duration = beats2[i] * tempo2; // length of note/rest in ms
|
|
|
+
|
|
|
+ if (notes2[i] == ' ') // is this a rest?
|
|
|
+ {
|
|
|
+ delay(duration); // then pause for a moment
|
|
|
+ }
|
|
|
+ else // otherwise, play the note
|
|
|
+ {
|
|
|
+ tone(buzzerPin, frequency(notes2[i]), duration);
|
|
|
+ delay(duration); // wait for tone to finish
|
|
|
+ }
|
|
|
+ delay(tempo2 / 10); // brief pause between notes
|
|
|
+ }
|
|
|
+}
|
|
|
+
|