Ian McKay
Published © LGPL

Wheel of Misfortune

The Wheel magically turns & dispenses the worst fates from all your favourite fairy tales. Complete with sound, lights and mythic automata.

AdvancedFull instructions providedOver 4 days2,073
Wheel of Misfortune

Things used in this project

Hardware components

Arduino Mega 2560 & Genuino Mega 2560
Arduino Mega 2560 & Genuino Mega 2560
×1
Arduino MP3 Shield
Adafruit Arduino MP3 Shield
×1
12V, 58RPM 60:1 Gear Motor w / Encoder
×1
Adafruit 16-Channel I2C Servo Controller
×1
Pololu Universal Aluminum 6mm Mounting Hub (4-40)
×1
Pololu 37D mm Metal Gearmotor Bracket
×1
Gel Medium acrylic paint
×1
Servos (Tower Pro MG996R)
×8
Dual H-Bridge motor drivers L298
SparkFun Dual H-Bridge motor drivers L298
×1
Copper Tape
×1
Diamond Head Nails
×1
RGB Diffused Common Anode
RGB Diffused Common Anode
×4
LED (generic)
LED (generic)
×2
12v/2amp power supply
×1
12v/5v voltage regulator
×1
magnets
×30

Story

Read more

Code

Wheel of Misfortune --- Arduino sketch

Arduino
Still a WIP, but mostly tuning and calibration.
// Source code for the Wheel of Misfortune (2016)
// Written by Jonah McKay
//
// A Monkey Dream Monkey Do project
// http://www.monkeydreammonkeydo.com
// Contact e-mail: ian@amimckay.com

//LIBRARIES START

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

//SOUND LIBRAIRIES START

#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>

//SOUND LIBRAIRIES END

//CAP TOUCH LIBRARY START

#include <CapacitiveSensor.h>

//CAP TOUCH LIBRARY END

//LIBRARIES END

//VARIABLES START

//SERVO VARIABLES BEGIN

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

#define MEDUSASERVOMIN  150 // this is the 'minimum' pulse length count (out of 4096)
#define MEDUSASERVOMAX  570 // this is the 'maximum' pulse length count (out of 4096)

#define SUBFATELEFTSERVOMIN  280 // this is the 'minimum' pulse length count (out of 4096)
#define SUBFATELEFTSERVOMAX  580 // this is the 'maximum' pulse length count (out of 4096)
#define SUBFATERIGHTSERVOMIN  200 // this is the 'minimum' pulse length count (out of 4096)
#define SUBFATERIGHTSERVOMAX  580 // this is the 'maximum' pulse length count (out of 4096)

#define ENDPOINTRIGHT 385 //CORRECT
#define ENDPOINTLEFT 455 //CORRECT

//SERVO VARIABLES END

//SOUND VARIABLES START

// These are the pins used for the music maker shield
#define SHIELD_RESET  -1      // VS1053 reset pin (unused!)
#define SHIELD_CS     7      // VS1053 chip select pin (output)
#define SHIELD_DCS    6      // VS1053 Data/command select pin (output)

// These are common pins between breakout and shield
#define CARDCS 4     // Card chip select pin
// DREQ should be an Int pin, see http://arduino.cc/en/Reference/attachInterrupt
#define DREQ 3       // VS1053 Data request, ideally an Interrupt pin

Adafruit_VS1053_FilePlayer musicPlayer = Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);

//SOUND VARIABLES END

//WHEEL VARIABLES START

int motorTicks = 0;
int motorTickLimit = 4;

const int enableA = 36;
const int motorA1 = 38;
const int motorA2 = 40;

long timeDestinationReached;

long timeRunUntilStarted = 0;

long timeZeroOutStarted = 0;

int motorStatus = 0;

//WHEEL VARIABLES END

//SENSOR PINS START

//MAG

const int magPin=30;

const int zeroMagPin=31;

boolean zeroOutStarted = false;

boolean lastMagSensorState = LOW;
boolean lastZeroMagSensorState = LOW;

boolean lastZeroWasStart = false;

long timeMagLastTriggered = 0;
long timeZeroMagLastTriggered = 0;

boolean zeroMagFound = false;
long timeZeroMagFound = 0;

int segmentsSinceLastZero = 0;

//CAP

CapacitiveSensor capacitiveTouchLeft = CapacitiveSensor(A10, A11);

CapacitiveSensor capacitiveTouchRight = CapacitiveSensor(A8, A9);

int leftCapNegativeIncrement = 0;
int rightCapNegativeIncrement = 0;

boolean leftCapActive = false;
boolean rightCapActive = false;

long timeLeftCapSet = 0;
long timeRightCapSet = 0;

const long requiredCapActiveTime = 1000;

const int capSensitivity = 55;

const int capIncrementLimit = 10;

//SENSOR PINS END

//LIGHT PINS START

const int redLEDS = 41;
const int greenLEDS = 37;
const int blueLEDS = 39;

const int whiteLeftLED = 8; //BREAKOUT
const int whiteRightLED = 9; //BREAKOUT

//LIGHT PINS END

//SUBFATE VARIABLES START

//Dragon, Troll, Witch, Owlbear, Wolves, Giant Spider, Goblins, Dogs, Rats, Insects
int eatenServoLocations[] = {216, 249, 280, 307, 342, 423, 458, 486, 516, 550};

//Lycanthrope, Zombie, Stone, Scarecrow, Raven, Frog, Amnesia, Madness, Sleep, Haunted
int mishapServoLocations[] = {282, 305, 345, 372, 398, 422, 495, 519, 543, 568};

int currentSubFate = 0;

boolean subFatePicked = false;

boolean subFateWandReturned = false;

boolean subFateWandAtDestination = false;

boolean subFateSoundPlayed = false;

long timeSubFateSoundEnded = 0;

//MEDUSA --

long timeMedusaStarted = 0;

boolean medusaDone = false;

//SUBFATE VARIABLES END

//SYSTEM VARIABLES START

boolean totalSilence = false; //disable ALL sounds if true

boolean wheelRunning = false;

boolean wheelActive = false;

boolean goingToFate = false;

boolean fateRunning = false;
boolean fateFinished = false;

long timeFateFinished = 0;

boolean wheelReturned = false;

long timeFateSoundEnded = 0;

boolean bootedUp = false;

int destinationResult;

bool destinationReached = false;

int fateLocations[] = {5000, 5500, 4500, 4250, 5250, 4000}; //miliseconds

bool fateSoundPlayed = false;

int zeroingOutForFate = false;

int fateDegreeLocations[] = {3, 44, 85, 126, 177, 183, 224, 265, 307, 357, 361}; //Last is to set off origin OBSOLETE?

int currentSegment = 0;

long lastWheelPositionTime = 0;

int segmentZeroStartedOn = 0;

long currentRunTime = 0;

bool foundStartDuringZero = false;

bool runningFate = false;

//Some Degree Location IDS:
//0: ? - beginning, or origin
//1: eaten
//2: blinded
//3: orphaned
//4: cused
//5: happy
//6: mishap
//7: poisoned
//8: imprisoned
//9: lost at sea
//10: While this should technically not be given to some functions, treat it like origin

//SYSTEM VARIABLES END

//IDLE VARIABLES START

boolean silentIdle = false; //disable sounds during idle if true

long timeOfLastIdleLightBlink = 0;

long timeOfLastTaunt = 0;

int waitForNextBlink = 200;

boolean currentIdleLightState;

//IDLE VARIABLES END
//DEBUG VARIABLES START

long timeSinceLastSerialDump = 0;

//DEBUG VARIABLES END

//VARIABLES END

void setup()
{
  //setup, runs once
  Serial.begin(9600);

  //SERVOS BEGIN --
  
  pwm.begin();
  
  pwm.setPWMFreq(60);  // Analog servos run at ~60 Hz updates

  //SERVOS END --

  //SOUND BOARD START --

   if (! musicPlayer.begin()) { // initialise the music player
     Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
     while (1);
  }
  Serial.println(F("VS1053 found"));
  
  SD.begin(CARDCS);    // initialise the SD card
  
  // Set volume for left, right channels. lower numbers == louder volume!
  musicPlayer.setVolume(20,20);

  // Timer interrupts are not suggested, better to use DREQ interrupt!
  //musicPlayer.useInterrupt(VS1053_FILEPLAYER_TIMER0_INT); // timer int

  // If DREQ is on an interrupt pin (on uno, #2 or #3) we can do background
  // audio playing
  musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);  // DREQ int
  
  //SOUND BOARD END --

  //WHEEL START --

  pinMode(enableA, OUTPUT);
  pinMode(motorA1, OUTPUT);
  pinMode(motorA2, OUTPUT);
  
  //WHEEL END --

  //SENSOR PINS START --

  //MAG
  
  pinMode(magPin, INPUT);
  pinMode(zeroMagPin, INPUT);
  
  //SENSOR PINS END --

  //LIGHT PINS START --

  pinMode(redLEDS, OUTPUT);
  pinMode(greenLEDS, OUTPUT);
  pinMode(blueLEDS, OUTPUT);
 
  //LIGHT PINS END --

  //SET RANDOM
  pinMode(A15, OUTPUT);
  randomSeed(analogRead(A15));
  
  yield();
}

void loop()
{
  //Start off by reading the Capacitive Touch sensors
  readCapTouch();
  updateCapTouchLights();


  //If they're both active...
  if (leftCapActive && rightCapActive)
  {
    //Check that they have:
    //    1: both been on for requiredCapActiveTime, or 1000 ms usually
    //    2: we are not already running the wheel
    //    3: and that we are reading the zeroMagPin, which means we're at Start 
    //       (could also mean we're at Happy, watch out!)
    if (timeLeftCapSet < millis()-requiredCapActiveTime && timeRightCapSet < millis()-requiredCapActiveTime && 
    !runningFate && digitalRead(zeroMagPin) == HIGH)
    {
      //If all that is true, then let's start the wheel!
      randomSeed(millis());
      setWhiteLights();
      chooseRandomDestination();
     // zeroingOutForFate = true;
      startWheelZeroOut();
      bootedUp = true;
      
    }
  }

  //If we're running the fate spin, then let's runCurrentDestination(), which handles
  //the nitty-gritty of all the spinning, loop() only needs to care about this function
  //for running the fate
  if (runningFate)
  {
    if (runCurrentDestination())
    {
      //If we're done, then say we're done, reset
      //variables, especially runningFate to false so we're not running
      //runCurrentDestination() more than once: see above if statement
      runningFate = false;
      fateFinished = false;
      destinationReached = false;
      timeFateFinished = millis();

      //If the zeroMagPin is high, then we can confidently say that
      //we're at start, the zero out should of handled this already,
      //but we can doubly-say that now.
      if (digitalRead(zeroMagPin) == HIGH)
          currentSegment = 0;
    }
  }
  //If we're not running a fate, then we're IDLING. Do some fancy light blinks,
  //and occasionally a taunt. IDLE LOOP CODE START --
  else
  {
    //Flicker the lights.
    if (millis()-timeOfLastIdleLightBlink > waitForNextBlink)
    {
      //If the light is currently on, then turn it off, and vice-versa.
      currentIdleLightState = !currentIdleLightState;
      
      if (currentIdleLightState) //on
          //Most of the time, set it to white lights...
          //Occasionally, (1 in 5) set it to either blue, or red.
          if (random(0, 5) <= 3)
              setWhiteLights();
          else
          {
              if (random(0, 2) == 0)
                  setBlueLights();
              else
                  setRedLights();
          }
                  
      else                   //off
          setOffLights();
      waitForNextBlink = random(100, 2000); //large variation keeps things interesting
      timeOfLastIdleLightBlink = millis();
    }

    //wait 30 seconds since both:
    //  1. the last taunt
    //  2. the wheel of fate stopped spinning
    //to taunt again
    if (millis()-timeOfLastTaunt > 30000 && millis() > timeFateFinished+30000)
    {
      tauntUser(); //also sets timeOfLastTaunt
    }
  }

  //END IDLE LOOP CODE --
  
  //Update/Read the magSensors every loop, so that we don't miss a segment
  //going by, remember the wheel is spinning _fast_
  updateMagSensors();

  
  //START DEBUG


  //Just print stuff to serial, specifically where it thinks it
  //is on the wheel and sub-fates every second
  if (timeSinceLastSerialDump < millis()-1000)
  {
    Serial.println("BEGIN SERIAL DUMP");
    printLocationName();
    printEatenSubFate();
    printMishapSubFate();
    timeSinceLastSerialDump = millis();
  }
  
  //END DEBUG
  
}


//START SOUND FUNCTIONS

void playSound(char path[])
{
  //playSound is basically just a wrapper function that takes a path name
  //and feeds it into the musicPlayer. If the totalSilence variable is true,
  //then it won't play. While the sound is running it also holds up everything
  //else.
  if (!totalSilence)
      musicPlayer.startPlayingFile(path);
  while (musicPlayer.playingMusic)
  {
    delay(10); //just wait
  }
}

//END SOUND FUNCTIONS


//START PRIMARY MOTOR FUNCTIONS --

void runMotor()
{
  //Runs the motor forwards (clockwise)
  digitalWrite(enableA, HIGH);
  digitalWrite(motorA1, HIGH);
  digitalWrite(motorA2, LOW);
}

void reverseMotor()
{
  //Runs the motor backwards (counter-clockwise)
  digitalWrite(enableA, HIGH);
  digitalWrite(motorA1, LOW);
  digitalWrite(motorA2, HIGH);
}

void stopMotor()
{
  //Stops the motor. Note that this doesn't automatically freeze the
  //wheel in place, it just makes it coast.
  digitalWrite(enableA, LOW);
  digitalWrite(motorA1, LOW);
  digitalWrite(motorA2, LOW);
}

void runWheelMotor(int motorSpeed)
{
  //Runs the wheel motor, sets the speed based on motorSpeed.
  //For best effect, run as many times as possible.
  //motorSpeed works like this: 1 is 50% forward, -1 is 50% backward,
  // 2 is 33% forward, so higher numbers are actually slower.
  //
  if (motorSpeed < 0)
  {
    if (motorTicks > motorTickLimit)
    {
      if (!wheelRunning && motorTicks > motorTickLimit*abs(motorSpeed) || wheelRunning)
      {
      wheelRunning = !wheelRunning;
      motorTicks = 0;
      }
    }
  
    if (wheelRunning)
    {
      reverseMotor();
    }
    else
    {
      stopMotor();
    }
    
    motorTicks++;
    motorStatus = -1;
  }
  else if (motorSpeed == 0)
  {
    stopMotor();
    motorStatus = 0;
  }
  else
  {
    if (motorTicks > motorTickLimit)
    {
      if (!wheelRunning && motorTicks > motorTickLimit*abs(motorSpeed) || wheelRunning)
      {
      wheelRunning = !wheelRunning;
      motorTicks = 0;
      }
    }
  
    if (wheelRunning)
    {
      runMotor();
    }
    else
    {
      stopMotor();
    }
    motorTicks++;
    motorStatus = 1;
  }
}

bool startWheelZeroOut()
{
  //Sets up variables for a wheel zero out. If the wheel is already
  //on Start, then it sets everything as though the wheel zero out
  //already happened, then returns true.
  segmentZeroStartedOn = currentSegment;
  timeZeroOutStarted = millis();
  
  if (digitalRead(zeroMagPin) == LOW || currentSegment != 0) //not already there
  {
    Serial.println("Wheel Zero Out - Not here aleady");
    zeroMagFound = false;
    zeroOutStarted = true;
    wheelReturned = false;
    foundStartDuringZero = false;
    timeZeroMagFound = 0;
    return false;
  }
  else //if we're already there
  {
    Serial.println("Wheel Zero Out - already here!");
    zeroMagFound = true;
    zeroOutStarted = false;
    wheelReturned = true;
    currentSegment = 0;
    zeroingOutForFate = false;
    lastZeroWasStart = true;
    foundStartDuringZero = true;
    timeZeroMagFound = 0;
  }
  return true;
}

boolean zeroOutWheel()
{
  //zero out wheel function, basically keep running it in a loop, and it should
  //update to try to reach start. If it starts in Lost at Sea or Imprisoned, then
  //every segment has the same speed. If not, then it slows down the wheel upon reaching
  //Lost at Sea. When it reaches start, depending on where it started, it gives it a jolt
  //back to try to keep it in Start, as opposed to keep coasting on.
  if (currentSegment != 0 && !foundStartDuringZero)
  {
    if (currentSegment < 7 || segmentZeroStartedOn > 7) //not lost at sea - not right next to it
            if (segmentZeroStartedOn <= 3)
                runWheelMotor(10);
            else if (segmentZeroStartedOn <= 4)
                runWheelMotor(9);
            else if (segmentZeroStartedOn <= 5)
                runWheelMotor(8);
            else if (segmentZeroStartedOn <= 6)
                runWheelMotor(9);
            else if (segmentZeroStartedOn >= 8) //special slowness for imprisoned and lost
                runWheelMotor(10);
            else
                runWheelMotor(7);
    else
        if (segmentZeroStartedOn <= 4 && currentSegment >= 7)
            if (segmentZeroStartedOn <= 2)
                 runWheelMotor(25);
            else
                 runWheelMotor(25);
        else if (segmentZeroStartedOn <= 6 && currentSegment >= 8) //If mishap or less, then start slow at imprisoned, else, lost at sea
            runWheelMotor(20);
        else if (segmentZeroStartedOn > 6 && currentSegment > 8)
            runWheelMotor(20);
        else
            runWheelMotor(14);
  }
  
  if (currentSegment == 0 || foundStartDuringZero)
  {
    foundStartDuringZero = true;
      if (!zeroMagFound)
      {
      zeroMagFound = true;
      timeZeroMagFound = millis();
      }
    if (timeZeroMagFound+230 > millis())
    {
        //give it a jolt back, power depending on where the
        //zero out started.
        if (segmentZeroStartedOn <= 3)
            runWheelMotor(-2);
        else if (segmentZeroStartedOn <= 6)
            runWheelMotor(-2);
        else if (segmentZeroStartedOn <= 8)
            runWheelMotor(-4);
        else
            runWheelMotor(-8);
    }
    else
    {
      //We've reached our destination, and jolted back. Shut down the motor,
      //set variables accordingly, and return true to let the calling function
      //know we're done.
      runWheelMotor(0);
          zeroMagFound = true;
          zeroOutStarted = false;
          wheelReturned = true;
          currentSegment = 0;
          zeroingOutForFate = false;
          lastZeroWasStart = true;
          return true;
          
    }
  }
  return false;
}

boolean spinWheelUntil(long timeStarted, int timeLimit)
{
  //Spins the wheel for a random amount of time (set by
  //chooseRandomDestination(), and put in through timeLimit
  //and timeStarted (timeStarted being when it started running,
  //timeLimit being how long it should run after that.
  //After it does that, then coast. Wait until it comes to a
  //full stop, then return true to let the caller function
  //know we're done. If it's ever on Start, then turn on the
  //motor so that it won't end on Start, giving it enough of
  //a boost to land on Eaten instead.
  //This function should be called frequently
  //in the runCurrentDestination() function itself, so that it
  //can also do things with lights and such if it
  //wishes.

  long currentTimePast = millis()-timeStarted;
  
  if (wheelActive)
  {
    if (currentTimePast < timeLimit/3)
    {
      runWheelMotor(2);
    }
    else if (currentTimePast < timeLimit)
    {
      runWheelMotor(4); //slow down when approaching the end
    }
    else
    {
      if (currentSegment != 0)
          runWheelMotor(0); //full coast
      else
          runWheelMotor(4); //If we're still on start, then give it some more boost to get out
    }

    if (currentTimePast > timeLimit && lastWheelPositionTime < millis()-1500)
    {
      //If we haven't changed our wheel position in a second and a half, then we're
      //at a complete stop, hopefully. Return true.
      return true;
    }
  }
  else
  {
    //DO SOME WAITING STUFF HERE TODO
  }

  return false;
}

void chooseRandomDestination()
{
  //Pick a random destination, and prepare everything for the runCurrentDestination()
  //function that will be called repeatedly in loop().
  wheelActive = true;
  goingToFate = true;
  runningFate = true;
  destinationReached = false;
  destinationResult = random(0, 6);
  timeRunUntilStarted = millis();
  currentRunTime = fateLocations[destinationResult]+random(-750, 750);
  lastWheelPositionTime = 0;

  //Reset to 0 HACK FIX TODO, because this function can only be called
  //while the zeroMag is HIGH, so this makes sense in context
  currentSegment = 0;
  lastZeroWasStart = true;
  timeZeroMagLastTriggered = millis();
  lastWheelPositionTime = millis();
  segmentsSinceLastZero = 0;
}

bool runCurrentDestination()
{
  //First, it zeroes out. Then it runs using spinWheelUntil for a random
  //amount of time (determined by chooseRandomDestination). Then, run
  //the right fate function, let that do it's thing until that returns
  //true. Then, after the fate is finished running, zero out again,
  //returning to start. After that, we can finally say we're done and
  //return true.
  
  if (!zeroingOutForFate)
  {
    if (goingToFate)
    {
      if (spinWheelUntil(timeRunUntilStarted, currentRunTime))
      {
        //We're done the main spin, spinWheelUntil returned true,
        //so let's start the fate now.
        Serial.println("NO MORE!");
        goingToFate = false;
        wheelActive = false;
        destinationReached = true;
        timeDestinationReached = millis();
      }
    }
    else if (!fateFinished)
    {
      //If the fate isn't finished, and we're not going to the fate, then
      //let's run the fate. Once it's returned true, we can finally say
      //the fate is done, and fateFinished = true;
      if (runFate(currentSegment, millis()-timeDestinationReached))
          fateFinished = true;
    }

    else
    {
      //If we're neither going to the fate, running the fate, or
      //zeroing out, then start the return zero out.
      Serial.println("Starting return back");
      startWheelZeroOut();
      zeroingOutForFate = true;
    }
  }
  else
  {
    //Zero out the wheel, returning it to start, both to prepare
    //and for returning...
    if (zeroOutWheel())
    {
      //If we're done zeroing out...
      Serial.println("Zero out finished");
      //Set the timeRunUntilStarted here, so spinWheelUntil() doesn't count the time spent zeroing out
      //against it's time limit.
      timeRunUntilStarted = millis();
      zeroingOutForFate = false;
      if (fateFinished)
      {
        //If the fate is finished, then that means that this was the return zero out,
        //meaning that this is the last thing that this function needs to do was
        //completed. Return true, our job is done.
        return true;
      }
    }
  }

  return false;
}

//END PRIMARY MOTOR FUNCTIONS --


//START SERVO FUNCTIONS --

boolean medusaServos(int spot)
{
  //Medusa servo function. Uses the spot function (takes in a variable based on a start
  //time and millis() to determine a timeline)
  int servoPosArr[] = {0, 0, 0, 0};
  if (spot > 3000)
  {
      return true;
  }
  else if (spot < 500)
  {
    servoPosArr[1] = map(spot, 0, 500, MEDUSASERVOMIN, MEDUSASERVOMAX-40);
    servoPosArr[2] = map(spot, 0, 500, MEDUSASERVOMIN, MEDUSASERVOMAX-280);
    servoPosArr[0] = map(abs(spot-500), 0, 500, MEDUSASERVOMIN+40, MEDUSASERVOMAX);
    servoPosArr[3] = map(abs(spot-500), 0, 500, MEDUSASERVOMIN+230, MEDUSASERVOMAX);
  }
  else if (spot > 500 && spot < 2000)
  {
    servoPosArr[1] = (MEDUSASERVOMAX-40)+random(-20-((spot-500)/3), 20); //top left
    servoPosArr[2] = (MEDUSASERVOMAX-280)+random(-20-((spot-500)/3), 20); //bottom left
    servoPosArr[0] = (MEDUSASERVOMIN+40)+random(-20, 20+((spot-500)/3)); //top right
    servoPosArr[3] = (MEDUSASERVOMIN+230)+random(-20, 20+((spot-500)/3)); //Bottom right
  }
  else if (spot > 2000 && spot < 2500)
  {
    servoPosArr[1] = map(abs((spot-2000)-500), 0, 500, MEDUSASERVOMIN, MEDUSASERVOMAX-40);
    servoPosArr[2] = map(abs((spot-2000)-500), 0, 500, MEDUSASERVOMIN, MEDUSASERVOMAX-280);
    servoPosArr[0] = map(abs(spot-2000), 0, 500, MEDUSASERVOMIN+40, MEDUSASERVOMAX);
    servoPosArr[3] = map(abs(spot-2000), 0, 500, MEDUSASERVOMIN+230, MEDUSASERVOMAX);
  }
  else if (spot > 2500)
  {
    servoPosArr[1] = MEDUSASERVOMIN;
    servoPosArr[2] = MEDUSASERVOMIN;
    servoPosArr[0] = MEDUSASERVOMAX;
    servoPosArr[3] = MEDUSASERVOMAX;
  }

  for (int x = 11; x < 15; x++)
  {
    pwm.setPWM(x, 0, servoPosArr[x-11]);
  }
  return false;
}

//END SERVO FUNCTIONS --

//START CAPACITIVE TOUCH FUNCTIONS --

void readCapTouch()
{
  //Read the capacitive touch sensors, and put set their variables
  //for use in loop()
  long capTotalLeft = capacitiveTouchLeft.capacitiveSensor(10);
  long capTotalRight = capacitiveTouchRight.capacitiveSensor(10);

  if (capTotalLeft > capSensitivity)
  {
    if (!leftCapActive)
        timeLeftCapSet = millis();
    
    leftCapNegativeIncrement = 0;
    leftCapActive = true;
    
  }

  //Basically, we have the capIncrementLimit so that one small value doesn't completely reset
  //the time count used to make sure the hands were pressed down for a second in loop()
  else
  {
    if (leftCapNegativeIncrement < capIncrementLimit)
    {
      leftCapNegativeIncrement++;
    }
    else
    {
      leftCapActive = false;
    }
  }

  if (capTotalRight > capSensitivity)
  {
    if (!rightCapActive)
        timeRightCapSet = millis();
    
    rightCapNegativeIncrement = 0;
    rightCapActive = true;
    
  }
  else
  {
    if (rightCapNegativeIncrement < capIncrementLimit)
    {
      rightCapNegativeIncrement++;
    }
    else
    {
      rightCapActive = false;
    }
  }
}

void updateCapTouchLights()
{
  //Updates the capTouch lights, nothing fancy, just turns on/off LEDS
  //based on true/false variables
  if (leftCapActive)
  {
    //Turns on left LED
    pwm.setPWM(whiteLeftLED, 0, 4095);
  }
  else
  {
    //Turns off left LED
    pwm.setPWM(whiteLeftLED, 0, 0);
  }

  if (rightCapActive)
  {
    //Turns on right LED
    pwm.setPWM(whiteRightLED, 0, 4095);
  }
  else
  {
    //Turns off right LED
    pwm.setPWM(whiteRightLED, 0, 0);
  }
}

//END CAPACITIVE TOUCH FUNCTIONS --

//START FATE FUNCTIONS --

//Fate functions, all of them return false if they're still running, and
//true if they're done. Most just run a sound, turn on a light, then wait
//for their time to run out, then return true, but some do servos, and notably
//Eaten and Mishap do sub-fates, and stand out from the rest.

boolean fate_orphaned(int spot)
{
  if (spot < 6000) //Also do servos for orphaned
  {
    setGreenLights();
    if (!fateSoundPlayed)
    {
      int soundResult = random(0, 2);
      if (soundResult == 0)
          playSound("orphan.mp3");
      else
          playSound("orphan2.mp3");
      fateSoundPlayed = true;
    }
    return false;
  }
  else
  {
    return true; //done
  }
}

boolean fate_lost(int spot)
{
  if (spot < 8000)
  {
    setBlueLights();
    if (!fateSoundPlayed)
    {
      int soundResult = random(0, 2);
      if (soundResult == 0)
          playSound("lost.mp3");
      else
          playSound("lost2.mp3");
      fateSoundPlayed = true;
    }
    return false;
  }
  else
  {
    return true; //done
  }
}

boolean fate_poisoned(int spot)
{
  if (spot < 5000)
  {
    setGreenLights();
    if (!fateSoundPlayed)
    {
      int soundResult = random(0, 2);
      if (soundResult == 0)
          playSound("poison.mp3");
      else
          playSound("poison2.mp3");
      fateSoundPlayed = true;
    }
    
    return false;
  }
  else
  {
    return true; //done
  }
}


boolean fate_cursed(int spot)
{
  if (spot < 5000)
...

This file has been truncated, please download it to see its full contents.

Credits

Ian McKay

Ian McKay

4 projects • 7 followers
Creative Technician & Possibilitist. Helping Artists bridge the Gap between Idea and Installation.

Comments