Escape from Arkham Asylum

Stanford ME 218a Fall 2012 Project

James Bensson, David Choy, Jason Gonzalez, Craig Western


Story

After his heroics in the movie The Dark Knight, Batman honorably took the blame for all the crimes that Harvey Dent (Two-Face) committed in order to preserve the pristine image of Gotham's former district attorney. Now on the top of Gotham's most-wanted-list, Batman must prepare himself for the worst-case scenario, being captured and thrown into Gotham's esteemed prison, Arkham Asylum. However, Batman is in luck! After consulting a team of Stanford engineers, Batman and his partner Robin had a Batman's Applied Technology Training scenario (BATTS) created to hone their escaping skills. Now they are able to workout on their own personalized Arkham Asylum Escape Training Module. This training module promises to hone their escaping capabilities in order to guarantee the heroes' freedom.

Function Description

User Inputs:

To begin the training module, one of the users must also press the green Bat ID Button. Once the action stages begin, the two-person training module teaches its users to be lightning fast when exercising the actions that are necessary for escaping a prison. These actions are as follows: punching and karate chopping guards, opening many doors, and choking out guard dogs. These actions are mimicked in the training module, where players must punch a button, karate chop through a sensor, open a door knob, and grip a handle.

Stages:

Waiting for Bat ID Stage:

In order to start the game module, one of the players must trigger the Bat ID, a green button at the front of the module. When the Bat ID signal is received, the first stage begins.

Stage 1:

In the first stage, the players compete against one another. A light illuminates a particular interaction area (button, sensor, or knob) and each player must complete the corresponding action. The player who completes the action first is awarded points and another action is randomly chosen for both players to perform. If a player executes an incorrect action, points are deducted. After the allotted time for stage 1, the players enter the second stage.

Stage 2:

In the second stage, the players compete against themselves. Based on how well each player performed in stage 1, he is given a certain amount of time to complete each action in stage 2 (the better a player does, the less time he gets). The idea here is to maximize each player's potential since one player may be far superior to his opponent. Again, a light illuminates a particular interaction area (button, sensor, or knob) and each player must complete the corresponding action. Note: each player may receive a different action. This time, in order to gain points and move to the next action, each player must complete his action within the amount of time allotted to him. If a player executes an incorrect action, points are deducted. After the allotted time for stage 2, the players enter the third stage.

Stage 3:

In the third stage, the players earn points based on how hard they grip their handle. More points are awarded to firmer grips. This continues until either one player has reached the maximum amount of points and wins, or the allotted time for stage 3 expires.

Reset Stage:

The reset stage signifies the end of the training session. After being in the reset stage for a certain amount of time, the module will automatically go back to the Waiting for Bat ID stage. The following situations cause the module to enter the reset stage: Throughout stages 1, 2, and 3, the timer keeps track of the time elapsed while the game is played. It denotes which stage the users are in until the game is over. Additionally, individual scores are kept for each player and are shown by the height of the respective Batman or Robin indicator.

Demo Video

Game Specifications/Requirements

  1. The BATTS will power up into a lockdown mode to maintain Bat-security. The only way to exit this lockdown mode and enter training scenario operation will be the use of a Bat ID device. The core of the Bat ID will be an SPDL-supplied module that emits IR radiation in a flashing pattern. The Bat ID that you build will trigger the module to produce the optical Bat-signal by grounding a pin in a manner of your own choosing. Your BATTS will be required to detect the optical signal to unlock and start its operation. The Bat ID (which is a separate device from your BATTS) must be functional at a minimum of 12" away from the detector.
    • Arkham Asylum Training Module: Employs a Bat ID signal to unlock the module.This Bat ID signal can be detected from over 12" away by a phototransistor.
  2. Both Batman and the shadowy figures from Batman's Rogues Gallery demand that the BATTSs should include interactions with two users. The head of Applied Sciences Lucius Fox has dictated that this requirement be met by assigning one C32 to each user's interactions.
    • Arkham Asylum Training Module: Is a two-player training module with one C32 assigned to monitor one user's interactions.
  3. Training is no good without a partner so your BATTS should not progress without continued input from the two users.
    • Arkham Asylum Training Module: After a certain lockdown time when one user does not have any interaction with the module, the whole training module will go into lockdown mode.
  4. Batman has limited training time available, so the average Caped Crusader should take approximately 30 seconds to interact with your BATTS. No one except Bane should be able to complete the process in less than 20 seconds. In the event that the users are unable to complete the training process within 45 seconds, the BATTS should stop interacting with them, return to lockdown mode and indicate that it is ready for new users.
    • Arkham Asylum Training Module: At its longest the game lasts 45 seconds before it goes into its reset stage. However, it is possible for one player to win in around 20 seconds.
  5. Your BATTS's interaction with the users should involve at least 3 distinct user interactions for each Bat-user.
    • Arkham Asylum Training Module: Contains four distinct user interactions for each user: pressing a button, swiping a sensor, turning a knob, and gripping a handle.
  6. The process of interacting with your BATTS should progress through several stages of training, with the BATTS responding differently in the different stages depending on either user performance or choices.
    • Arkham Asylum Training Module: Contains three different action stages. During the first two stages the module only responds to the button, knob, and swipe interactions. If the user selects the correct interaction, points are awarded. If the incorrect interaction is chosen, points are deducted. In the third stage the module only responds to the grip sensor. Different point values are awarded to different grip strength.
  7. Your BATTS should require large scale Bat-motion on the part of the user for at least one of its interactions.
    • Arkham Asylum Training Module: The user has to swipe past a sensor. This has been categorized as a large scale motion.
  8. At least 2 of the interactions with the user must produce different outcomes based on the training stage of the BATTS.
    • Arkham Asylum Training Module: In the first two stages, if the user selects the correct interaction, points are awarded. If the incorrect interaction is selected, points are deducted.
  9. Each BATTS should include a creative display of the passage of time. No 7-segment displays.
    • Arkham Asylum Training Module: Displays the passage of time with a clock-like second had that moves as time passes.
  10. Each BATTS should include an indication of when it is active (and therefore responsive to interactions) and when it is locked down.
    • Arkham Asylum Training Module: When it is active, music is playing. When it is inactive no noise is playing.
  11. When the users complete the interaction, the BATTS should provide an exciting audio and/or visual experience that will inspire hope, or strike fear into the hearts of criminals (or citizens) everywhere.
    • Arkham Asylum Training Module: When the interaction is completed, inspiring Batman music is played. Several colorful LEDs are flashed on the side of the user that won.
  12. Each 'C32 in the BATTS should react not only to events that it detects directly, but also to events seen by the other 'C32 and to the passage of time. Each 'C32 should be capable of conveying at least 8 distinct messages to the other 'C32.
    • Arkham Asylum Training Module: The two C32s send 8 distinct messages to each other. These are as follows: start game, next stage button next, next stage twist next, next stage swipe next, action completed button next, action completed twist next, action completed swipe next, and game over.
  13. Interactions with a user on one 'C32 must influence what is happening with the user on the other 'C32.
    • Arkham Asylum Training Module: In stage 1, when one user inputs the correct interaction before the other user, both user interactions transition to the next interaction.
  14. The BATTS must be usable without human instruction. Any static instructions must be only in pictorial form (Think: Ikea assembly instructions).
    • Arkham Asylum Training Module: All instructions given pictorially.
  15. Each BATTS must have parts that visibly move under the control of each of the C32s.
    • Arkham Asylum Training Module: The clock and both player scorecards move under the control of the C32s.
  16. Each team must construct a BATTS. While it is permissible to use consumer devices as components, the covert, secret-identity-protecting nature of the Applied Sciences Lab requires that such devices must be substantially modified before incorporation into your project. We don't want you to just buy significant portions of your project. If there is any question as to whether or not the purchased component has been modified significantly enough, please see the teaching staff.
    • Arkham Asylum Training Module: All circuits were created by the inventors of the module.
  17. At least one of the user interactions with each C32 must be interpreted as an analog input from the user.
    • Arkham Asylum Training Module: The grip handle works as an analog input.
  18. At least one of the user interactions with each C32 must involve non-contact sensing.
    • Arkham Asylum Training Module: The swipe sensor acts as a non-contact sensor.
  19. Each BATTS must provide the user with feedback about his/her actions. The feedback must include at least one of: haptic/audio/tactile feedback. Multiple modes of feedback, including modes not listed here, are encouraged.
    • Arkham Asylum Training Module: The users of the module are given feedback with the music tracks that play during the game. Also, the score cards allow the users to track their progress.
  20. The complete BATTS must be a self contained entity, capable of meeting all specifications while connected only to the project power supply that will be provided.
    • Arkham Asylum Training Module: Is a stand alone module that is only powered by the provided power supply.
  21. In order to fit into the trunk of the Batmobile, the BATTS MUST fit into a footprint no more than 18" wide by 18" deep by 36" high. During operation, the user interaction may occupy no more than a 24" wide x 18" deep x 80" high volume in front of the BATTS. Two BATTSs must both be usable while sitting together on one of the 5' wide tables in our classroom. The entire BATTS must be easily and safely moved from the construction site to the grading session and then again to the Atrium for the presentations. Make sure that you plan for this.
    • Arkham Asylum Training Module: Meets the hardware design specifications described above.

Schematics

Bat ID:

The Bat ID detector circuit comprised of an IR phototransistor in a transresistive circuit, a high-pass filter, and a comparator with hysteresis. The sizing of the feedback resistor in the transresistive circuit ensured a signal amplitude large enough to allow detection of the Bat ID from up to a foot away, meeting the specifications. The high pass filter reduced the voltage levels caused by background infrared radiation, allowing the Bat ID detector to work in daytime and at night. The hysteresis band on the comparator circuit was sized appropriately to reject noise but provide clean rising and falling edges that match the IR signal. The output was then connected to the Port M5 pin of one C32 so that the incoming pulses can be decoded as Morse characters.

Audio:

The audio module that provided the auditory user feedback was the SOMO-14D by 4D systems. Tracks can be loaded onto MicroSD cards in an appropriate file format and with specific names and then inserted into the SOMO-14D. This module provides two options for operation. One option, known as key-mode, detects falling edges and can be used to play/pause, select next track, or select previous track. In serial-mode, serial commands are sent to the SOMO-14D through the DATA pin in sync with pulses on the CLK pin. When specific tracks are desired, the audio track addresses are sent to the SOMO-14D, and the matching track is played. Although key-mode provides for the easiest code, it does not provide the flexibility and control given by serial-mode. Therefore, the C32 was connected to the SOMO-14D in serial-mode, with DATA going to Port AD2 and CLK going to Port AD3, allowing for direct selection of tracks.

Resistors were placed in-line with the DATA and CLK input pins of the SOMO-14D, bringing the 5 volt signals from the C32 down to safe levels for the audio module. This was necessary because the SOMO-14D operates at 3.3V, which was brought in to the module's VCC pin from a 3.3 V regulator tied to 5V. The BUSY pin was connected to Port AD4, which was configured as an input, in order to detect when tracks were being played. The BUSY pin is brought low when tracks are playing. The SOMO-14D was only connected to one C32.

Servos:

The timer and score servos were connected to almost identical circuits. In order to reduce noise on the rest of the C32 circuits caused by servo twitching, 5 volts was provided to the servos through a 5V voltage regulator. This was connected to a 13.8V power supply. Capacitors were also used to reduce potential circuit noise. The only difference between the two servo circuits is that the score servo was replicated and connected to the Port T7 pin on both C32's while there was only one timer servo circuit and it was connected to the Port T6 pin on only one of the C32's.

LEDs:

The LED circuits were very simple. One green LED indicator light was used to signal each user action prompt (Grip, Swipe, Knob, and Button). The only exception to this was the button indicator LED, which was a red LED that was integrated to the button hardware. In order to light each LED individually, they were each driven by a 2N7000 N-channel MOSFET with a current limiting resistor. Appropriate brightness was achieved by sizing the resistor to allow the maximum allowed current through the LEDs given a 5V power supply. The winning LED indicators were driven differently. Since this circuit needed to drive 4 LEDs (two red and two yellow), the LEDs were driven by an IRLZ34N N-channel power MOSFET since it was already on-hand. The red and yellow LEDs were placed into pairs in series with a current-limiting resistor, and these pairs were then placed in parallel. These LED circuits were replicated for each C32, providing matching indicator lights for each user.

Swipe:

The swipe circuit resembles the Bat ID circuit with a few notable exceptions. Like the Bat ID detector, it also has an IR phototransistor in a transresistive circuit. It also has a comparator with hysteresis. The two main differences (aside from resistor values) are the lack of the high-pass filter between the OpAmp and the comparator and an IR LED connected in series with a current-limiting resistor. The IR LED is always on when the C32 is powered. This LED provides the light that the phototransistor needs to detect. The LED and phototransistor are mounted roughly a quarter inch away from each other and pointing in the same direction. With this configuration, a hand swipe in front of the LED/phototransistor pair provides a reflective surface for the infrared light to bounce from the LED back to the phototransistor. The high-pass filter was removed because the swipe sensor did not need to detect incoming pulses, but instead just needs to be pulled high when a hand is present in front of the LED/phototransistor pair. Therefore, the comparator hysteresis was sized such that reflected light from up to 4 inches away can be detected by the swipe circuit while the maximum background IR light levels won't trigger a high pulse. The swipe circuit is replicated for each side, and the output of the swipe circuit is connected to Port AD6 pin on each C32.

Grip:

The grip circuit is comprised of two force sensitive resistors (FSRs) connected together in parallel and then placed in series with a fixed resistor. The resulting voltage divider circuit is placed between 5V and ground, with the central node between the variable and fixed resistors connected to the Port AD7 pin of each C32. This configuration was chosen to provide for the greatest range of grip detection with an appropriately sized fixed resistor. With no pressure on the FSRs, their resistance is essentially infinite when compared to the fixed resistor. Therefore, with no pressure, the C32 reads an analog voltage very close to 0V. As pressure is applied to one or both of the FSRs, the variable resistance of the FSRs drops to levels on the same order of magnitude as the fixed resistor. This brings the voltage level read by the C32 to vary between 0-5V, depending on the amount of force applied. The FSRs vary in a very non-linear fashion, allowing for moderate levels of grip pressure (such as the grip of a small child) to be detected along with strong grips (that of an adult). Furthermore, by placing the FSRs in parallel, the circuit is able to detect any amount of grip force at any point along the wooden grip sensors. These two characteristics allow for a lot of variability in user performance.

Knob:

The twist knob circuit involved a clever hack of two standard doorknobs. Limit switches were glued to the back of each doorknob such that a full twist of the doorknob in either direction would trigger one of the limit switches. These limit switches were then placed in parallel, with one side connected to 5V and the other connected to Port AD5 of each C32. A pull-down resistor was used to ensure a voltage reading of 0V when the doorknobs were in a neutral position and a reading of 5V when either one of the limit switches was triggered by a full doorknob twist. Debouncing was achieved in software.

Button:

The button circuit was a standard single-pole, single-throw, momentary switch circuit, with one side of the button switch connected to 5 V and the other connected to Port T0 of each C32. A pull-down resistor was used to ensure a voltage reading of 0V when the button was not depressed and a reading of 5V when the button was depressed. Debouncing was achieved in software.

C32 pin assignments

Because the C32's were programmed with the same code, the C32 pin assignments had to be the same for both circuits. This meant that some pins were essentially unused as certain components only existed in one of the circuits. Below are the pin assignments for both of the C32's as laid out by the ribbon cable connector. Note that only one of the two C32s had a BatID detector (M5), a timer servo (T6), and an audio module circuit (AD2-4) connected to those pins. The other C32 had those pins floating, but otherwise the C32 circuits are identical.

Communcation Lines

By pulling all the data lines high, the state of the data lines was used to mimic the communication strobe line. The data lines were set to outputs and the corresponding lines were pulled low to send data to the other C32. The data lines were set to default to inputs, so the message was received and the acknowledge line was raised to indicate the message was received. Upon receiving an acknowledge, the C32 that sent the message would raise the data lines high again and set them back to inputs to listen for any messages. A timer was used to help alleviate communcation clashes and ensure the message was sent.

Drawings

Original Concept Sketch:

In developing the mechanical design and layout of the device, a conceptual design was first created to meet all game specifications. All sensing inputs (button, open door, swipe) would be located on a panel within easy reach of the user, and two grips would be developed and placed on the front of the device. LEDs would indicate when users should take a specific action. Servos would control a timer and two scorekeeping components that would move vertically along the front of the device, driven by a pulley system located behind the top panel. The Bat ID would initiate operation of the device by pointing it at a phototransistor located between the grippers and pressing a button to initiate the decoding process.
Drawings of the final construction, which involved only minor changes to the conceptual design, are shown below. A wooden frame was built to support the lower half of the device, and 1/8 in. Duron paneling was attached to complete the structure. Doorknobs were mounted and equipped with limit switches for the "open door" action. The "swipe" action was moved to the side of the device in order to avoid false readings as users complete other actions. Because the button agreed upon for implementation included an LED, the external indicator LED for the button was eliminated. The Bat ID was implemented by mounting the phototransistor and Bat ID on the rear of the device and requiring the user to press a button between the two grips to begin the training module. On the upper panel, Batman and Robin logos were placed along vertical tracks for scorekeeping, and two sets of four LEDs were included at the top to indicate that a player has won the game. Views and drawings of the final construction before painting and addition of vinyl graphics are shown below, with dimensions in inches.

Front View:

Side View:

Top View:

Base:

It was decided early in the design process that a structurally robust base would be necessary to withstand interaction with users unfamiliar with the training module, as the game instructions encouraged aggressive gameplay. The base was designed and implemented using two-by-fours and standard fasteners and brackets, making for an extremely strong foundation.

Grip:

The grip was designed to allow the placement of force-sensitive resistors in the center of a rod that could be easily gripped by the user while accounting for the wires that would run through the device to the resistors. The concept was implemented for each grip using two halves of a 1.5-in. dowel, 1/8-in. thickness rubber, and an L-bracket. Wood was removed using a Dremel tool and rubber was cut to leave room for cabling. Resistors were glued to flat surfaces between the two halves, allowing gripping force to be detected along the length of the rod.

Pulleys:

Batman and Robin logos used to track score on the front of the machine were driven by Vigor VTS-08B servo motors as shown below. (The timer arrow, not shown, was attached to a Hextronik HXT900 servo motor mounted directly to the front panel.) A lever arm was attached perpendicular to the servo axis and connected to a pulley configuration in a 1 to 2 distance ratio (i.e., an inch of translation of the end of the lever arm tied to the pulley yields two inches of logo translation). This was done in order to achieve the desired linear range of motion of the logos given the limited range of motion of the servos. Counterweights were tied to the logos in order to ensure string tension and weighted such that force on the motors did not exceed the 56 oz-in. specified for the servo. Components were arranged to allow about 10 in. of linear motion for the logos on the front of the device.

Pseudo-code

Audio.c

Module Variables:
    TickCount
    AudioCommand
    hasQueuedAudioCommand
    QueuedAudioCommand

sendAudioCommand
    If TickCount < 0
        Only start sending new command if tick count is negative
        This means the last command was fully sent

        Set command asAudioCommand
        Set TickCount as 0
        Bring AUDIO_CLK_PIN low
    Else
        Queue new command into QueuedAudioCommand
        Set hasQueuedAudioCommand

audioTick
    If TickCount < 0
        Waiting for new command to send, nothing to do
        Return
    Else if TickCount is 0
        Start sending audio command sequence
        Bring AUDIO_CLK_PIN low
    Else if TickCount is 1
        Do nothing
    Else if TickCount is between 2 and 33, inclusive
        If TickCount is even
            Bring AUDIO_CLK_PIN low
        If TickCount is odd
            Calculate which bit to load (MSB first)
            Set AUDIO_DATA_PIN based on bit of command
            Bring AUDIO_CLK_PIN high
    Else if TickCount is 34
        Do nothing
    Else if TickCount is 35
        Set TickCount as -2
        If there is a command queued
            Set TickCount as -1
            Set AudioCommand as QueuedAudioCommand
    Increment TickCount

ButtonDebouncSM.c

Module Variables
    CurrentState
    MyPriority

InitButtonDebounceSM
    Initialize the MyPriority variable with the passed in parameter
    Set CurrentState to be DEBOUNCING
    Start debounce timer
    Post the initial transition event
    Return whether the event posted correctly

RunButtonDebounceSM
    If the debouncing timer timed out
        Set current state as READY2SAMPLE;
    Else if event type is START_DEBOUNCE
        Start debounce timer
        Set current state to DEBOUNCING

DecodeMorseSM.c

Module Variables
    CurrentState
    MyPriority
    MorseString[]
    KnownString[]
    KnownStringPosition
    legalChars[]
    morseCode[]
    index

InitDecodeMorseSM
    Initialize the MyPriority variable with the passed in parameter
    Clear the Morse character
    Post the initial transition event
    Return whether the event posted correctly

RunDecodeMorseSM
    If the index exceeds MORSE_LENGTH
        Clear Morse character
    If a dot was detected
        If index < MORSE_LENGTH
            Add dot to MorseString
            Increment index
    If a dash was detected
        If index < MORSE_LENGTH
            Add dash to MorseString
            Increment index
    If an EOC was detected
        If we received a valid character
            If character matches character we're searching for
                Increment KnownStringPosition
                If we've found all the characters
                    Start game
                    Reset the position of character we're searching for
            Else
                Begin search from beginning
                KnownStringPosition = 0;
            Clear Morse character
    If an EOW was detected
        If we received a valid character
        Clear character

ClearMorseChar
    Set index to 0
    Set MorseString to empty string

DecodeMorse
    For each legal character
        If MorseString matches Morse representation of legal character
            Return corresponding legal character
        Else
            Return error character

EventCheckers.c

Check4BatID
    Get current pin state
    If current pin state is different than last state
        If pin state is high
            Post RISING_EDGE to MorseElements
        If pin state is low
            Post FALLING_EDGE to MorseElements
    Store current pin state as last state

Check4ButtonAndTwist
    If button state machine state is READY2SAMPLE
        Post START_DEBOUNCE event to Button
        Get current button pin state
        Get current twist pin state
        If current button state is different than last state
            If pin state is high
                Post BUTTON_DOWN event to Game
        If current twist state is different than last state
            If pin state is high
                Post KNOB_TWISTED event to Game
        Store current button pin state as last button state
        Store current knob pin state as last knob state
    If not ready to sample button
        Do nothing

Check4Swipe
    Get current swipe pin state
    If current reflection state is different than last state
        If pin state is high
            Post SWIPE_DETECTED event to Game
    Store current reflection state as current pin state

Check4Grip
    If grip is ready to be sampled
        Post START_DEBOUNCE event to Grip
        Get current grip value
        If current pressure is different than last pressure
            Post GRIP_AMOUNT event to Game with amount as EventParam
        Store last grip amount as current grip amount

Check4Message
    If we're the one sending the message
        If acknowledge line is high
            Post MESSAGE_CLEAR event to communication module
        Return
    
    Read all 4 pins as set as current state
    If current binary number of pins is different than last number
        If number is 0000
            Post START_GAME event to Game
            Set acknowledge line as output
            Set acknowledge line high
        If number is 0001
            Post NEXT_STAGE_BUTTON_NEXT event to Game
            Set acknowledge line as output
            Set acknowledge line high
        If number is 0010
            Post NEXT_STAGE_TWIST_NEXT event to Game
            Set acknowledge line as output
            Set acknowledge line high
        If number is 0011
            Post NEXT_STAGE_SWIPE_NEXT event to Game
            Set acknowledge line as output
            Set acknowledge line high
        If number is 0101
            Post ACTION_COMPLETED_BUTTON_NEXT event to Game
            Set acknowledge line as output
            Set acknowledge line high
        If number is 0110
            Post ACTION_COMPLETED_TWIST_NEXT event to Game
            Set acknowledge line as output
            Set acknowledge line high
        If number is 0111
            Post ACTION_COMPLETED_SWIPE_NEXT event to Game
            Set acknowledge line as output
            Set acknowledge line high
        If number is 1000
            Post GAME_OVER event to Game
            Set acknowledge line as output
            Set acknowledge line high
        If number is 1001
            Post LOCKDOWN event to Game
            Set acknowledge line as output
            Set acknowledge line high
        If number is 1111
            Set acknowledge line low
            Set acknowledge line as output
    Store last message pin state as current pin state

GameSM.c

Module Variables:
    CurrentState
    MyPriority
    Stage2IndividualTime;
    Points;

InitGameSM
    Store priority number
    Generate first random number
    Initialize all lights
    Turn off all action LEDs
    Set current state as WaitingForBattID
    Turn off win LEDs
    Initialize to no time elapsed
    Set pins 6 and 7 as servo outputs for timer and score
    Initialize Time servo
    Initialize Score Servo

RunGameSM
    If event type is timeout from total game timer
        Send a tick to the audio module
        If we reached 1 second and we're not reset or waiting for BATT ID
            Increment time ticks
            Reset sub-second tick
            Move servo of physical timer
        Reset timer
    If current state is WaitingForBattID
        If event type is START_GAME
            Select next action
            Set next state as Stage1xx based on action chosen
            Present NEXT_STAGE_xx_NEXT to other C32
            Turn on corresponding action LED
            Start first track of music
            Start stage_1 timer
            Start lockdown timer
            Start total_game timer
        If event type is NEXT_STAGE_BUTTON_NEXT
            Set next state as Stage1xx based on event type
            Perform start stage 1 using next action
        If event type is NEXT_STAGE_TWIST_NEXT
            Set next state as Stage1xx based on event type
            Perform start stage 1 using next action
        If event type is NEXT_STAGE_SWIPE_NEXT
            Set next state as Stage1xx based on event type
            Perform start stage 1 using next action
    If current state is Stage1Button
        If event type is GAME_OVER
            Set current state as Reset
            Turn off all action LEDs
            Stop music
            Start reset timer
        If event type is LOCK_DOWN
            Set current state as Reset
            Perform lockdown
        If event type is ES_TIMEOUT and parameter is lockdown timer
            Set current state as Reset
            Turn off all action LEDs
            Present LOCKDOWN to other C32
            Stop music
            Start reset timer
        If event type is ES_TIMEOUT and parameter is stage_1 timer
            Select next action
            Set next state as Stage2xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Present NEXT_STAGE_xx to other C32
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is NEXT_STAGE_BUTTON_NEXT
            Set next state as Stage2xx based on event
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is NEXT_STAGE_TWIST_NEXT
            Set next state as Stage2xx based on event
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is NEXT_STAGE_SWIPE_NEXT
            Set next state as Stage2xx based on event
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is ACTION_COMPLETED_BUTTON_NEXT
            Set next state as Stage1xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
        If event type is ACTION_COMPLETED_TWIST_NEXT
            Set next state as Stage1xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
        If event type is ACTION_COMPLETED_SWIPE_NEXT
            Set next state as Stage1xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
        If event type is correct action BUTTON_DOWN
            Select next action
            Set next state as Stage1xx based on action chosen
            Add points
            If points >= maximum
                Set current state as Reset
                Turn on win LEDs
                Start reset timer
                Turn off all action LEDs
                Present GAME_OVER to other C32
                Play end_of_game music
            Else
                Turn off all action LEDs
                Turn on corresponding action LED
                Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                Reset lockdown timer
        If event type is wrong type KNOB_TWISTED or SWIPE_DETECTED
            Subtract points
            Reset lockdown timer
    If current state is Stage1Twist
        If event type is GAME_OVER
            Set current state as Reset
            Turn off all action LEDs
            Stop music
            Start reset timer
        If event type is LOCK_DOWN
            Set current state as Reset
            Perform lockdown
        If event type is ES_TIMEOUT and parameter is lockdown timer
            Set current state as Reset
            Turn off all action LEDs
            Present GAME_OVER to other C32
            Stop music
            Start reset timer
        If event type is ES_TIMEOUT and parameter is stage_1 timer
            Select next action
            Set next state as Stage2xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
            Present NEXT_STAGE_xx to other C32
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is NEXT_STAGE_BUTTON_NEXT
            Set next state as Stage2xx based on event
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is NEXT_STAGE_TWIST_NEXT
            Set next state as Stage2xx based on event
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is NEXT_STAGE_SWIPE_NEXT
            Set next state as Stage2xx based on event
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is ACTION_COMPLETED_BUTTON_NEXT
            Set next state as Stage1xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
        If event type is ACTION_COMPLETED_TWIST_NEXT
            Set next state as Stage1xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
        If event type is ACTION_COMPLETED_SWIPE_NEXT
            Set next state as Stage1xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
        If event type is correct action :  KNOB_TWISTED
            Select next action
            Set next state as Stage1xx based on action chosen
            Add points
            If points >= maximum
                Set current state as Reset
                Turn on win LEDs
                Start reset timer
                Turn off all action LEDs
                Present GAME_OVER to other C32
                Play end_of_game music
            Else
                Turn off all action LEDs
                Turn on corresponding action LED
                Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                Reset lockdown timer
        If event type is wrong type BUTTON_DOWN or SWIPE_DETECTED
            Subtract points
            Reset lockdown timer
    If current state is Stage1Swipe
        If event type is GAME_OVER
            Set current state as Reset
            Turn off all action LEDs
            Stop music
            Start reset timer
        If event type is LOCK_DOWN
            Set current state as Reset
            Perform lockdown
        If event type is ES_TIMEOUT and parameter is lockdown timer
            Set current state as Reset
            Turn off all action LEDs
            Present GAME_OVER to other C32
            Stop music
            Start reset timer
        If event type is ES_TIMEOUT and parameter is stage_1 timer
            Select next action
            Set next state as Stage2xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
            Present NEXT_STAGE_xx to other C32
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is NEXT_STAGE_BUTTON_NEXT
            Set next state as Stage2xx based on event
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is NEXT_STAGE_TWIST_NEXT
            Set next state as Stage2xx based on event
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is NEXT_STAGE_SWIPE_NEXT
            Set next state as Stage2xx based on event
            Turn off all action LEDs
            Turn on corresponding action LED
            Calculate individual_action timer time
            Select next track of music
            Start individual_action timer
            Start stage_2 timer
        If event type is ACTION_COMPLETED_BUTTON_NEXT
            Set next state as Stage1xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
        If event type is ACTION_COMPLETED_TWIST_NEXT
            Set next state as Stage1xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
        If event type is ACTION_COMPLETED_SWIPE_NEXT
            Set next state as Stage1xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED
        If event type is correct action :  SWIPE_DETECTED
            Select next action
            Set next state as Stage1xx based on action chosen
            Add points
            If points >= maximum
                Set current state as Reset
                Turn on win LEDs
                Start reset timer
                Turn off all action LEDs
                Present GAME_OVER to other C32
                Play end_of_game music
            Else
                Turn off all action LEDs
                Turn on corresponding action LED
                Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                Reset lockdown timer
        If event type is wrong type :  BUTTON_DOWN or KNOB TWISTED
            Subtract points
            Reset lockdown timer
    If current state is Stage2Button
        If event type is ES_TIMEOUT and event parameter is stage_2 timer
            Set next state as STAGE_3
            Turn off action LEDs
            Turn on corresponding action LED
            Select next track of music
        If event type is LOCK_DOWN
            Set current state as Reset
            Perform lockdown
        If event is GAME_OVER
            Set current state as Reset
            Start reset timer
            Turn off all action LEDs
            Stop music
            playMusicTrack(NextState);
        If event type is ES_TIMEOUT and parameter is lockdown timer
            Set current state as Reset
            Turn off all action LEDs
            Present GAME_OVER to other C32
            Stop music
            Start reset timer
        If event type is correct action : BUTTON_DOWN
            Select next action
            Set next state as Stage2xx based on action chosen
            Add points
            If points >= maximum
                Set current state as Reset
                Turn on win LEDs
                Start reset timer
                Turn off all action LEDs
                Present GAME_OVER to other C32
                Play end_of_game music
            Else
                Turn off all action LEDs
                Turn on corresponding action LED
                Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                Reset lockdown timer
        If event type is wrong type KNOB_TWISTED or SWIPE_DETECTED
            Subtract points
            Reset lockdown timer
        If event type is ES_TIMEOUT and event parameter is individual_action timer
            Select next action
            Set next state as Stage2xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED for next action
            Reset individual_action timer
    If current state is Stage2Twist
        If event type is ES_TIMEOUT and event parameter is stage_2 timer
            Set next state as STAGE_3
            Turn off action LEDs
            Turn on corresponding action LED
            Select next track of music
        If event is GAME_OVER
            Set current state as Reset
            Start reset timer
            Turn off all action LEDs
            Stop music
        If event type is LOCK_DOWN
            Set current state as Reset
            Perform lockdown
        }
        If event type is ES_TIMEOUT and parameter is lockdown timer
            Set current state as Reset
            Turn off all action LEDs
            Present GAME_OVER to other C32
            Stop music
            Start reset timer
        If event type is correct action : KNOB_TWISTED
            Select next action
            Add points
            If points >= maximum
                Set current state as Reset
                Turn on win LEDs
                Start reset timer
                Turn off all action LEDs
                Present GAME_OVER to other C32
                Play end_of_game music
            Else
                Turn off all action LEDs
                Turn on corresponding action LED
                Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                Reset lockdown timer
        If event type is wrong type BUTTON_DOWN or SWIPE_DETECTED
            Subtract points
            Reset lockdown timer
        If event type is ES_TIMEOUT and event parameter is individual_action timer
            Select next action
            Set next state as Stage2xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED for next action
            Reset individual_action timer
    If current state is Stage2Swipe
        If event type is ES_TIMEOUT and event parameter is stage_2 timer
            Set next state as STAGE_3
            Turn off action LEDs
            Turn on corresponding action LED
            Select next track of music
        If event is GAME_OVER
            Set current state as Reset
            Start reset timer
            Turn off all action LEDs
            Stop music
        If event type is LOCK_DOWN
            Set current state as Reset
            Perform lockdown
        If event type is ES_TIMEOUT and parameter is lockdown timer
            Set current state as Reset
            Turn off all action LEDs
            Present GAME_OVER to other C32
            Stop music
            Start reset timer
        If event type is correct action : SWIPE_DETECTED
            Select next action
            Set current state as Stage2xx based on action chosen
            Add points
            If points >= maximum
                Set current state as Reset
                Turn on win LEDs
                Start reset timer
                Turn off all action LEDs
                Present GAME_OVER to other C32
                Play end_of_game music
            Else
                Turn off all action LEDs
                Turn on corresponding action LED
                Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                Reset lockdown timer
        If event type is wrong type BUTTON_DOWN or KNOB_TWISTED
            Subtract points
            Reset lockdown timer
        If event type is ES_TIMEOUT and event parameter is individual_action timer
            Select next action
            Set next state as Stage2xx based on action chosen
            Turn off all action LEDs
            Turn on corresponding action LED for next action
            Reset individual_action timer
    If current state is STAGE_3
        If game is over based on exceeding 45 seconds
            Set current state as Reset
            Start reset timer
            Turn off all action LEDs
            Stop music
        If event type is ES_TIMEOUT and parameter is lockdown timer
            Set current state as Reset
            Turn off all action LEDs
            Present GAME_OVER to other C32
            Stop music
            Start reset timer
        If event type is GAME_OVER
            Set current state as Reset
            Turn off all action LEDs
            Stop music
            Start reset timer
        If event type is LOCK_DOWN
            Set current state as RESET
            Perform lockdown
        If event type is GRIP_AMOUNT
            Add points based on grip amount
            If points >= maximum
                Set current state as Reset
                Turn on win LEDs
                Start reset timer
                Turn off all action LEDs
                Present GAME_OVER to other C32
                Play end_of_game music
            Reset lockdown timer
    If current state is Reset
        If event type is ES_TIMEOUT and parameter is LED_flash timer
            If points >= maximum
                Flip state on win LEDs
        If event type is ES_TIMEOUT and parameter is reset timer
            Turn win LEDs off
            Reset score servo
            Set points to 0
            Reset timer servo
            Set next state to WAITING_FOR_BATID
            Reset music (stop music, reset music to first track)
            Stop TOTAL_GAME_TIMER
    Set current state as next state

selectNextAction
    Generate random number
    Map random number to action
    Switch based on action number
        If action number is 0
            If stage is stage 1
                Return stage 1 button
            If stage is stage 2
                Return stage 2 button
        If action number is 1
            If state is stage 1
                Return stage 1 twist
            If stage is stage 2
                Return stage 2 twist
        If action number is 2
            If stage is stage 1
                Return stage 1 swipe
            If stage is stage 2
                Return stage 2 swipe

initLights
    Set button LED as output
    Set swipe LED as output
    Set twist LED as output
    Set grip LED as output
    Set win LED as output

lightActionLED
    Turn all LEDs off
    If action is button
        Turn on button LED
    If action is twist
        Turn on twist LED
    If action is swipe
        Turn on swipe LED
    If action is grip
        Turn on grip LED
    If going to the reset stage
        Turn off all action LEDs

lightWinLEDs
    If state is true
        Turn on LEDs
    If state is false
        Turn off LEDs

sendMessageToC32
    Switch based on message to send
        If message is to start game
            Post START_GAME event
        If message is to go to next stage with button action next
            Post NEXT_STAGE_BUTTON_NEXT
        If message is to go to next stage with twist next
            Post NEXT_STAGE_TWIST_NEXT
        If message is to go to next stage with swipe next
            Post NEXT_STAGE_SWIPE_NEXT
        If message is action completed with button next
            Post ACTION_COMPLETED_BUTTON_NEXT
        If message is action completed with twist next
            Post ACTION_COMPLETED_TWIST_NEXT
        If message is action completed with swipe next
            Post ACTION_COMPLETED_SWIPE_NEXT
        If message is game over
            Post GAME_OVER
        If message is lockdown
            Post LOCKDOWN

determineMessage
    If currently waiting for Batt ID messages
        If going to Stage1Button
            Return MESSAGE_NEXT_STAGE_BUTTON_NEXT
        If going to Stage1Twist
            Return MESSAGE_NEXT_STAGE_TWIST_NEXT
        If going to Stage1Swipe
            Return MESSAGE_NEXT_STAGE_SWIPE_NEXT
    If currently in stage 1
        If going to Stage1Button
            Return MESSAGE_ACTION_COMPLETED_BUTTON_NEXT
        If going to Stage1Twist
            Return MESSAGE_ACTION_COMPLETED_TWIST_NEXT
        If going to Stage1Swipe
            Return MESSAGE_ACTION_COMPLETED_SWIPE_NEXT
        If going to Stage1Button
            Return MESSAGE_NEXT_STAGE_BUTTON_NEXT
        If going to Stage1Twist
            Return MESSAGE_NEXT_STAGE_TWIST_NEXT
        If going to Stage1Swipe
            Return MESSAGE_NEXT_STAGE_SWIPE_NEXT

playMusicTrack
    Switch based on NextState
        If state is WaitingForBattID
            Stop music
        If state is Stage1Button
            Select state 1 music ("0000.ad4")
            Play music
        If state is Stage1Twist
            Select state 1 music ("0000.ad4")
            Play music
        If state is Stage1Swipe
            Select state 1 music ("0000.ad4")
            Play music
        If state is Stage2Swipe
            Select stage 2 music ("0001.ad4")
        If state is Stage2Twist
            Select stage 2 music ("0001.ad4")
        If state is Stage2Swipe
            Select stage 2 music ("0001.ad4")
        If state is Stage3
            Select stage 3 music ("0002.ad4")
        If state is Reset
            Select end of game music ("0003.ad4")

calculateStage2Time
    Time is scaled from max time as fraction of MAX_POINTS earned

addToPoints
    If answer was incorrect
        Set points to max of 0 and points - wrong answer points
    If answer was correct
        If stage is 1
            Add stage 1 points
        If stage is 2
            Add stage 2 points
    If ponts > max ponts
        Set points equal to max points
    Set score servo

gripAddToPoints
    Add points
    If points > max points
        Set points as max points
    Set score servo

performLockdown
    Turn off all action LEDs
    Stop music
    Start reset timer

performStartStage1_ActionNext
    Turn on corresponding action LED
    Start first track of music
    Start lockdown timer
    Start total_game timer

GripSM.c

Module Variables:
    CurrentState
    MyPriority

InitGripSM
    Initialize the MyPriority variable with the passed in parameter.
    Set CurrentState to be DEBOUNCING
    Start debounce timer (timer posts to GripSM)

RunGripSM
    If event type is ES_TIMEOUT
        If the debouncing timer timed out
            Set current state to ready to sample
            Also post event to GameSM
    If EventType is START_DEBOUNCE
        Start timer (timer posts to GripSM)
        Set current state to DEBOUNCING

MessageC32SM.c

Module Variables:
    CurrentState
    MyPriority
    LastMessageSent

InitMessageC32SM
    Store priority number
    Initialize all comm ports as inputs
    Set the current state to GettingMessage

RunMessageC32SM
    Set default next state as current state
    If event type is ES_NEW_KEY
        Don't do anything, so return
    If waiting for incoming message
        If message timer times out
            Repost last message event
        If event type is START_GAME
            Set comm ports as outputs
            Set 0x00
            Save last message sent
        If event type is START_GAME
            Set comm ports as outputs
            Save last message sent
        If event type is NEXT_STAGE_TWIST_NEXT
            Set comm ports as outputs
            Set 0x02
            Save last message sent
        If event type is NEXT_STAGE_SWIPE_NEXT
            Set comm ports as outputs
            Set 0x03
            Save last message sent
        If event type is ACTION_COMPLETED_BUTTON_NEXT
            Set comm ports as outputs
            Set 0x05
            Save last message sent
        If event type is ACTION_COMPLETED_TWIST_NEXT
            Set comm ports as outputs
            Set 0x06
            Save last message sent
        If event type is ACTION_COMPLETED_SWIPE_NEXT
            Set comm ports as outputs
            Set 0x07
            Save last message sent
        If event type is GAME_OVER
            Set comm ports as outputs
            Set 0x08
            Save last message sent
        If event type is LOCKDOWN
            Set comm ports as outputs
            Set 0x09
            Save last message sent
        If event type is MESSAGE_CLEAR
            Should not be here, so just return
        Set next state as SendingMessage
    If current state is SendingMessage
        If message timer times out
            Set comm ports as inputs
            Set next state as GettingMessage
            Start message timer
        If event type is MESSAGE_CLEAR
            Set back to no event (1111) state on data pins
            Turn data pins back into inputs
            Set next state as GettingMessage
    Save next state as current state

MorseElementsSM.c

Module Variables:
    MyPriority
    FirstDelta
    SecondDelta
    TimeOfLastFall
    TimeOfLastRise
    LengthOfDot

InitMorseElementsSM
    Store priority number
    Put us into the Initial PseudoState
    Set FirstDelta as 0
    Post initial transition event

RunMorseElementsSM
    Switch based on current state
        If state is InitMorseElements
            If event type is ES_INIT
                Set next state to CalWaitForRise
        If state is CalWaitForRise
            If event type is RISING_EDGE
                Save time of last rise
                Set next state to CalWaitForFall
            If event type is CALIBRATION_COMPLETE
                Set next state to EOC_WaitRise
        If state is CalWaitForFall
            If event type is FALLING_EDGE
                Save time of last fall
                Set next state to CalWaitForRise
                Check calibration
        If state is EOC_WaitRise
            If event type is RISING_EDGE
                Save time of last rise
                Set next state to EOC_WaitFall
                Characterize space
        If state is EOC_WaitFall
            If event type is FALLING_EDGE
                Save time of last fall
                Set next state as EOC_WaitRise
            If event type is EOC_DETECTED
                Set next state as DecodeWaitFall
        If state is DecodeWaitRise
            If event type is RISING EDGE
                Save time of last rise
                Set next state as DecodeWaitFall
                Characterize space
        If state is DecodeWaitFall
            If event type is FALLING_EDGE
                Save time of last fall
                Set next state as DecodeWaitRise
                Characterize pulse
    Update the current state to next state

TestCalibration
    If FirstDelta is 0
        Save first delta as difference in times of last fall and rise
    Else
        Save second delta as difference in times of last fall and rise
        If first delta is about a third of second delta
            Post calibration complete event
            Save length of dot as first delta
        If first delta is about three times the length of second delta
            Post calibration complete event
            Save length of dot as second delta
        If two deltas are too similar
            Set first delta as second delta

CharacterizeSpace
    Set last interval as difference in time of last rise and fall
    If last interval was a character space
        Post EOC_DETECTED event
    If last interval was a word space
        Post EOW_DETECTED event

CharacterizePulse
    Set last interval as difference in time of last fall and rise
    If last interval was a dot length
        Post DOT_DETECTED event
    If last interval was a dash length
        Post DASH_DETECTED event
    Else
        Post BAD_PULSE event

Code & Header Listings

Both microcontrollers ran the same code, however, a master/slave implementation was set up depending on which microcontroller received the Bat ID signal. The one that does becomes the master and controls the audio, the game timer, and the stage timer.

Headers

Audio.h

#ifndef AUDIO_H
#define AUDIO_H

// Event Definitions
#include "const.h"

// Public Function Prototypes
void sendAudioCommand(unsigned int command);
void audioTick(void);

#endif /* AUDIO_H */

ButtonDebounceSM.h

#ifndef ButtonDebounceSM_H
#define ButtonDebounceSM_H

// Event Definitions
#include "ES_Configure.h"
#include "ES_Types.h"
#include "const.h"

// typedefs for the states
// State definitions for use with the query function
typedef enum { READY2SAMPLE, DEBOUNCING } ButtonState ;

// Public Function Prototypes
boolean InitButtonDebounceSM(uint8_t Priority);
boolean PostButtonDebounceSM(ES_Event ThisEvent);
ES_Event RunButtonDebounceSM(ES_Event ThisEvent);
ButtonState QueryButtonDebounceSM (void);


#endif /* ButtonDebounceSM_H */

const.h

#ifndef CONST_H
#define CONST_H

#include <ME218_C32.h>

#define BUTTON_IO_PORT DDRT
#define BUTTON_PORT PTT
#define BUTTON_PIN BIT0HI

#define COMM_ACK_IO_PORT DDRT
#define COMM_ACK_PORT PTT
#define COMM_ACK_PIN BIT1HI

#define COMM1_IO_PORT DDRT
#define COMM1_PORT PTT
#define COMM1_PIN BIT2HI

#define COMM2_IO_PORT DDRT
#define COMM2_PORT PTT
#define COMM2_PIN BIT3HI

#define COMM3_IO_PORT DDRT
#define COMM3_PORT PTT
#define COMM3_PIN BIT4HI

#define COMM4_IO_PORT DDRT
#define COMM4_PORT PTT
#define COMM4_PIN BIT5HI

#define TIMER_IO_PORT DDRT
#define TIMER_PORT PTT
#define TIMER_PIN BIT6HI
#define TIMER_PIN_NUMBER 6

#define SCORE_IO_PORT DDRT
#define SCORE_PORT PTT
#define SCORE_PIN BIT7HI
#define SCORE_PIN_NUMBER 7


#define BUTTON_LED_IO_PORT DDRM
#define BUTTON_LED_PORT PTM
#define BUTTON_LED_PIN BIT0HI

#define TWIST_LED_IO_PORT DDRM
#define TWIST_LED_PORT PTM
#define TWIST_LED_PIN BIT1HI

#define SWIPE_LED_IO_PORT DDRM
#define SWIPE_LED_PORT PTM
#define SWIPE_LED_PIN BIT2HI

#define GRIP_LED_IO_PORT DDRM
#define GRIP_LED_PORT PTM
#define GRIP_LED_PIN BIT3HI

#define WIN_LED_IO_PORT DDRM
#define WIN_LED_PORT PTM
#define WIN_LED_PIN BIT4HI

#define BATT_ID_IO_PORT DDRM
#define BATT_ID_PORT PTM
#define BATT_ID_PIN BIT5HI

// Output: use PTAD
#define AUDIO_DATA_PORT PTAD
#define AUDIO_DATA_PIN BIT2HI

// Output: use PTAD
#define AUDIO_CLK_PORT PTAD
#define AUDIO_CLK_PIN BIT3HI

// Input: use PTIAD
#define AUDIO_BUSY_PORT PTIAD
#define AUDIO_BUSY_PIN BIT4HI

// Input: use PTIAD
#define TWIST_PORT PTIAD
#define TWIST_PIN BIT5HI

// Input: use PTIAD
#define SWIPE_PORT PTIAD
#define SWIPE_PIN BIT6HI

// Grip is analog input
#define GRIP_PIN 7

#define MESSAGE_START_GAME 0x00
#define MESSAGE_NEXT_STAGE_BUTTON_NEXT 0x01
#define MESSAGE_NEXT_STAGE_TWIST_NEXT 0x02
#define MESSAGE_NEXT_STAGE_SWIPE_NEXT 0x03
#define MESSAGE_ACTION_COMPLETED_BUTTON_NEXT 0x05
#define MESSAGE_ACTION_COMPLETED_TWIST_NEXT 0x06
#define MESSAGE_ACTION_COMPLETED_SWIPE_NEXT 0x07
#define MESSAGE_GAME_OVER 0x08
#define MESSAGE_LOCKDOWN 0x09
#define MESSAGE_NO_EVENT 0x0F

// Time constants
// 45 seconds = 43945
// 20 seconds = 19531
// 15 seconds = 14648
// 10 seconds = 9799
// 1 second = 980
// 500 milliseconds = 488
// 20 milliseconds = 19
#define TOTAL_GAME_TIME 43945
#define STAGE_1_TIME 14648
#define STAGE_2_TIME 9766
#define LOCKDOWN_TIME 9799
#define MESSAGE_TIME 19
#define RESET_TIME 9799
#define WIN_LED_TIME 244
#define _1_SEC 980
#define DEBOUNCE_TIME 30

// Point Constants
#define MAX_POINTS 1175

// Miscellaneous Enumeration Constants
#define ON 1
#define OFF 0
#define CORRECT 1
#define INCORRECT 0

// Transiton Constants
#define STAGE1 1
#define STAGE2 2
#define STAGE3 3
#define TRANSITION 4
#define SAME_STAGE 5

#endif

DecodeMorseSM.h

#ifndef DecodeMorseSM_H
#define DecodeMorseSM_H

// Event Definitions
#include "ES_Configure.h"
#include "ES_Types.h"
#include "const.h"

// typedefs for the states
// State definitions for use with the query function
typedef enum { InitDecodeMorse } DecodeState ;

// Public Function Prototypes
boolean InitDecodeMorseSM(uint8_t Priority);
boolean PostDecodeMorseSM(ES_Event ThisEvent);
ES_Event RunDecodeMorseSM(ES_Event ThisEvent);
DecodeState QueryDecodeMorseSM(void);

#endif /* DecodeMorseSM_H */

ES_Configure.h

#ifndef CONFIGURE_H
#define CONFIGURE_H

/****************************************************************************/
// The maximum number of services sets an upper bound on the number of
// services that the framework will handle. Reasonable values are 8 and 16
// HOWEVER: at this time only a value of 8 is supported.
#define MAX_NUM_SERVICES 8

/****************************************************************************/
// This macro determines that nuber of services that are *actually* used in
// a particular application. It will vary in value from 1 to MAX_NUM_SERVICES
#define NUM_SERVICES 6

/****************************************************************************/
// These are the definitions for Service 0, the lowest priority service
// every Events and Services application must have a Service 0. Further
// services are added in numeric sequence (1,2,3,...) with increasing
// priorities
// the header file with the public fuction prototypes
#define SERV_0_HEADER "GameSM.h"
// the name of the Init function
#define SERV_0_INIT InitGameSM
// the name of the run function
#define SERV_0_RUN RunGameSM
// How big should this services Queue be?
#define SERV_0_QUEUE_SIZE 5

/****************************************************************************/
// The following sections are used to define the parameters for each of the
// services. You only need to fill out as many as the number of services
// defined by NUM_SERVICES
/****************************************************************************/
// These are the definitions for Service 1
#if NUM_SERVICES > 1
// the header file with the public fuction prototypes
#define SERV_1_HEADER "ButtonDebounceSM.h"
// the name of the Init function
#define SERV_1_INIT InitButtonDebounceSM
// the name of the run function
#define SERV_1_RUN RunButtonDebounceSM
// How big should this services Queue be?
#define SERV_1_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 2
#if NUM_SERVICES > 2
// the header file with the public fuction prototypes
#define SERV_2_HEADER "MorseElementsSM.h"
// the name of the Init function
#define SERV_2_INIT InitMorseElementsSM
// the name of the run function
#define SERV_2_RUN RunMorseElementsSM
// How big should this services Queue be?
#define SERV_2_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 3
#if NUM_SERVICES > 3
// the header file with the public fuction prototypes
#define SERV_3_HEADER "DecodeMorseSM.h"
// the name of the Init function
#define SERV_3_INIT InitDecodeMorseSM
// the name of the run function
#define SERV_3_RUN RunDecodeMorseSM
// How big should this services Queue be?
#define SERV_3_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 4
#if NUM_SERVICES > 4
// the header file with the public fuction prototypes
#define SERV_4_HEADER "MessageC32SM.h"
// the name of the Init function
#define SERV_4_INIT InitMessageC32SM
// the name of the run function
#define SERV_4_RUN RunMessageC32SM
// How big should this services Queue be?
#define SERV_4_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 5
#if NUM_SERVICES > 5
// the header file with the public fuction prototypes
#define SERV_5_HEADER "GripSM.h"
// the name of the Init function
#define SERV_5_INIT InitGripSM
// the name of the run function
#define SERV_5_RUN RunGripSM
// How big should this services Queue be?
#define SERV_5_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 6
#if NUM_SERVICES > 6
// the header file with the public fuction prototypes
#define SERV_6_HEADER "TestService.h"
// the name of the Init function
#define SERV_6_INIT TestServiceInit
// the name of the run function
#define SERV_6_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_6_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 7
#if NUM_SERVICES > 7
// the header file with the public fuction prototypes
#define SERV_7_HEADER "TestService.h"
// the name of the Init function
#define SERV_7_INIT TestServiceInit
// the name of the run function
#define SERV_7_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_7_QUEUE_SIZE 3
#endif

/****************************************************************************/
// the name of the posting function that you want executed when a new
// keystroke is detected.
// The default initialization distributes keystrokes to all state machines
#define POST_KEY_FUNC ES_PostAll

/****************************************************************************/
// Name/define the events of interest
// Universal events occupy the lowest entries, followed by user-defined events
typedef enum {  ES_NO_EVENT = 0,
                ES_ERROR,  /* used to indicate an error from the service */
                ES_INIT,   /* used to transition from initial pseudo-state */
                ES_NEW_KEY, /* signals a new key received from terminal */
                ES_TIMEOUT, /* signals that the timer has expired */
                /* User-defined events start here */
                START_GAME,
                NEXT_STAGE_BUTTON_NEXT,
                NEXT_STAGE_TWIST_NEXT,
                NEXT_STAGE_SWIPE_NEXT,
                ACTION_COMPLETED_BUTTON_NEXT,
                ACTION_COMPLETED_TWIST_NEXT,
                ACTION_COMPLETED_SWIPE_NEXT,
                BUTTON_DOWN,
                KNOB_TWISTED,
                SWIPE_DETECTED,
                GAME_OVER,
                GRIP_AMOUNT,
                LOCKDOWN,
                START_DEBOUNCE,
                RISING_EDGE,
                FALLING_EDGE,
                CALIBRATION_COMPLETE,
                EOC_DETECTED,
                EOW_DETECTED,
                DOT_DETECTED,
                DASH_DETECTED,
                BAD_PULSE,
                MESSAGE_CLEAR} ES_EventTyp_t ;

/****************************************************************************/
// These are the definitions for the Distribution lists. Each definition
// should be a comma seperated list of post functions to indicate which
// services are on that distribution list.
#define NUM_DIST_LISTS 0
#if NUM_DIST_LISTS > 0
#define DIST_LIST0 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 1
#define DIST_LIST1 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 2
#define DIST_LIST2 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 3
#define DIST_LIST3 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 4
#define DIST_LIST4 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 5
#define DIST_LIST5 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 6
#define DIST_LIST6 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 7
#define DIST_LIST7 PostTemplateFSM
#endif

/****************************************************************************/
// This are the name of the Event checking funcion header file.
#define EVENT_CHECK_HEADER "EventCheckers.h"

/****************************************************************************/

// This is the list of event checking functions
#define EVENT_CHECK_LIST Check4BatID, Check4ButtonAndTwist, Check4Grip, Check4Swipe, Check4Message, Check4Grip

/****************************************************************************/
// These are the definitions for the post functions to be executed when the
// correspnding timer expires. All 8 must be defined. If you are not using
// a timers, then you can use TIMER_UNUSED
#define TIMER_UNUSED ((pPostFunc)0)
#define TIMER0_RESP_FUNC PostGameSM
#define TIMER1_RESP_FUNC PostGameSM
#define TIMER2_RESP_FUNC PostGameSM
#define TIMER3_RESP_FUNC PostGripSM
#define TIMER4_RESP_FUNC PostGameSM
#define TIMER5_RESP_FUNC PostMessageC32SM
#define TIMER6_RESP_FUNC PostButtonDebounceSM
#define TIMER7_RESP_FUNC PostGameSM

/****************************************************************************/
// Give the timer numbers symbolc names to make it easier to move them
// to different timers if the need arises. Keep these definitons close to the
// definitions for the response functions to make it easier to check that
// the timer number matches where the timer event will be routed

#define TOTAL_GAME_TIMER 0
#define STAGE_TIMER 1
#define LOCKDOWN_TIMER 2
#define LED_FLASH_TIMER 3
#define RESET_TIMER 4
#define MESSAGE_TIMER 5
#define DEBOUNCE_TIMER 6
#define INDIVIDUAL_TIMER 7

#endif /* CONFIGURE_H */

EventCheckers.h

boolean Check4BatID(void);
boolean Check4Grip(void);
boolean Check4ButtonAndTwist(void);
boolean Check4Swipe(void);
boolean Check4Grip(void);
boolean Check4Message(void);

#include <bitdefs.h>
#include "const.h"

GameSM.h

#ifndef GAMESM_H
#define GAMESM_H

// Event Definitions
#include "ES_Configure.h"
#include "ES_Types.h"
#include "const.h"

// typedefs for the states
// State definitions for use with the query function
typedef enum { WaitingForBattID, Stage1Button, Stage1Twist,
             Stage1Swipe, Stage2Button, Stage2Twist,
             Stage2Swipe, Stage3, Reset } GameState_t ;

// Public Function Prototypes
boolean InitGameSM(uint8_t Priority);
boolean PostGameSM(ES_Event ThisEvent);
ES_Event RunGameSM(ES_Event ThisEvent);
GameState_t QueryGameSM(void);

#endif /* GAMESM_H */

GripSM.h

#ifndef GRIPSM_H
#define GRIPSM_H

// Event Definitions
#include "ES_Configure.h"
#include "ES_Types.h"
#include "const.h"

// typedefs for the states
// State definitions for use with the query function
typedef enum { READY2SAMPLE_GRIP, DEBOUNCING_GRIP } GripState ;

// Public Function Prototypes

boolean InitGripSM(uint8_t Priority);
boolean PostGripSM(ES_Event ThisEvent);
ES_Event RunGripSM(ES_Event ThisEvent);
GripState QueryGripSM(void);

#endif /* GRIPSM_H */

MessageC32SM.h

#ifndef MessageC32SM_H
#define MessageC32SM_H

// Event Definitions
#include "ES_Configure.h"
#include "ES_Types.h"
#include "const.h"

// typedefs for the states
// State definitions for use with the query function
typedef enum { SendingMessage, GettingMessage } MessageC32State_t ;

// Public Function Prototypes

boolean InitMessageC32SM(uint8_t Priority);
boolean PostMessageC32SM(ES_Event ThisEvent);
ES_Event RunMessageC32SM(ES_Event ThisEvent);
MessageC32State_t QueryMessageC32SM(void);

#endif /* MessageC32SM_H */

MorseElementsSM.h

#ifndef MorseElementsSM_H
#define MorseElementsSM_H

// Event Definitions
#include "ES_Configure.h"
#include "ES_Types.h"
#include "const.h"

// typedefs for the states
// State definitions for use with the query function
typedef enum { InitMorseElements, CalWaitForRise,
    CalWaitForFall, EOC_WaitRise, EOC_WaitFall,
    DecodeWaitRise, DecodeWaitFall } ElementState ;

// Public Function Prototypes
boolean InitMorseElementsSM(uint8_t Priority);
boolean PostMorseElementsSM(ES_Event ThisEvent);
ES_Event RunMorseElementsSM(ES_Event ThisEvent);
ElementState QueryMorseElementsSM(void);

//Defining the Morse signal on M1
#define MORSE_SIGNAL_HIGH BIT1HI
#define MORSE_SIGNAL_LOW BIT1LO

#endif /* MorseElementsSM_H */

Sources

Audio.c

/****************************************************************************
 Module
   Audio.c

 Revision
   1.0

 Description
   This is a module for playing audio.

****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this state machine as well as any machines at the
   next lower level in the hierarchy that are sub-machines to this machine
*/
#include "Audio.h"
#include <stdio.h>

/*----------------------------- Module Defines ----------------------------*/

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/


/*---------------------------- Module Variables ---------------------------*/

// Store what part of sequence we're currently in
static char TickCount = -1;

// Store the audio command that is currently being sent
static unsigned int AudioCommand;

// Store a queued audio command
static unsigned char hasQueuedAudioCommand = 0;
static unsigned int QueuedAudioCommand;


/*------------------------------ Module Code ------------------------------*/

/****************************************************************************
 Function
     sendAudioCommand

 Parameters
     unsigned int command : Command to send to audio module

 Returns
     Nothing

 Description
     Stores the command to send and begins the sending process

****************************************************************************/
void sendAudioCommand(unsigned int command)
{
    if (TickCount < 0)
    {
        // Only start sending new command if tick count is negative
        // This means the last command was fully sent

        AudioCommand = command;
        TickCount = 0;

        // Bring AUDIO_CLK_PIN low
        AUDIO_CLK_PORT &= ~AUDIO_CLK_PIN;
    }
    else
    {
        // Queue new command
        hasQueuedAudioCommand = 1;
        QueuedAudioCommand = command;
    }
}

/****************************************************************************
 Function
     audioTick

 Parameters
     Nothing

 Returns
     Nothing

 Description
     Function to send actual audio commands to the module in pieces

****************************************************************************/
void audioTick(void)
{
    if (TickCount < 0)
    {
        // Waiting for new command to send, nothing to do
        return;
    }
    else if (TickCount == 0)
    {
        // Start sending audio command sequence
        // Bring AUDIO_CLK_PIN low
        AUDIO_CLK_PORT &= ~AUDIO_CLK_PIN;
    }
    else if (TickCount == 1)
    {
        // Done waiting 1 tick (1ms)
    }
    else if (TickCount >= 2 && TickCount <= 33)
    {
        // Done waiting 2 ticks (2ms)

        // Pulse in data bit based on tick count
        // If tick count is even, bring clock low
        // If tick count is odd, set data, and bring clock high
        if ((TickCount % 2) == 0)
        {
            // Bring AUDIO_CLK_PIN low
            AUDIO_CLK_PORT &= ~AUDIO_CLK_PIN;
        }
        else
        {
            // Calculate which bit to load (MSB first)
            char k = 16 - (TickCount - 1)/2;
            // Set AUDIO_DATA_PIN
            if (((AudioCommand & (1 << k)) >> k) != 0)
            {
                AUDIO_DATA_PORT |= AUDIO_DATA_PIN;
            }
            else
            {
                AUDIO_DATA_PORT &= ~AUDIO_DATA_PIN;
            }
            // Bring AUDIO_CLK_PIN high
            AUDIO_CLK_PORT |= AUDIO_CLK_PIN;
        }
    }
    // Hold clock line high for 2ms before trying to send new commands
    else if (TickCount == 34)
    {
        // Done waiting 1ms
    }
    else if (TickCount == 35)
    {
        // Done waiting 2ms, able to send new audio command
        TickCount = -2;

        // Select new audio command if queued, begin sending
        if (hasQueuedAudioCommand)
        {
             hasQueuedAudioCommand = 0;
             TickCount = -1;
             AudioCommand = QueuedAudioCommand;
        }
    }
    TickCount++;
}

ButtonDebounceSM.c

/****************************************************************************
 Module
   ButtonDebounceSM.c

 Description
   This is the state machine for debouncing buttons using the
   Gen2 Events and Services Framework.

****************************************************************************/
/*----------------------------- Include Files -----------------------------*/

#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ButtonDebounceSM.h"
#include <ME218_C32.h>
#include "const.h"
#include <stdio.h>

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/

/*---------------------------- Module Variables ---------------------------*/
// everybody needs a state variable, you may need others as well.
// type of state variable should match that of enum in header file
static ButtonState CurrentState;

// with the introduction of Gen2, we need a module level Priority var as well
static uint8_t MyPriority;

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitButtonDebounceSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, sets up the initial transition

****************************************************************************/
boolean InitButtonDebounceSM(uint8_t Priority)
{
    ES_Event ThisEvent;

    // Initialize the MyPriority variable with the passed in parameter.
    MyPriority = Priority;

    // Set CurrentState to be DEBOUNCING
    CurrentState = DEBOUNCING;

    // Start debounce timer (timer posts to ButtonDebounceSM)
    ES_Timer_InitTimer(DEBOUNCE_TIMER, DEBOUNCE_TIME);

    // End of InitializeButton (return True)
    // Post the initial transition event
    ThisEvent.EventType = ES_INIT;
    if (ES_PostToService(MyPriority, ThisEvent) == True)
    {
        return True;
    }
    else
    {
        return False;
    }
}

/****************************************************************************
 Function
      PostButtonDebounceSM

 Parameters
     ES_Event ThisEvent : the event to post to the queue

 Returns
     boolean, False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
****************************************************************************/
boolean PostButtonDebounceSM(ES_Event ThisEvent)
{
    return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunButtonDebounceSM

 Parameters
   ES_Event : the event to process

 Returns
   ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
   Implements the two state state machine for RunDebounce
   2 States: ready2sample and debouncing
****************************************************************************/
ES_Event RunButtonDebounceSM(ES_Event ThisEvent)
{
    ES_Event ReturnEvent;
    ReturnEvent.EventType = ES_NO_EVENT; // assume no error


    if (ThisEvent.EventType == ES_TIMEOUT)
    {
        // If the debouncing timer timed out
        if (ThisEvent.EventParam == DEBOUNCE_TIMER)
        {
            // Set current state to READY2SAMPLE
            CurrentState = READY2SAMPLE;
        }
    }
    else if (ThisEvent.EventType == START_DEBOUNCE)
    {
        // Start debounce timer (timer posts to ButtonDebounceSM)
        ES_Timer_InitTimer(DEBOUNCE_TIMER, DEBOUNCE_TIME);

        // Set current state to DEBOUNCING
        CurrentState = DEBOUNCING;
    }

    return ReturnEvent;
}

/****************************************************************************
 Function
     QueryButtonDebounceSM

 Parameters
     Nothing

 Returns
     ButtonState, The current state of the Button state machine

 Description
     returns the current state of the ButtonDebounceSM

****************************************************************************/
ButtonState QueryButtonDebounceSM (void)
{
   return(CurrentState);
}

DecodeMorseSM.c

/****************************************************************************
 Module
   DecodeMorseSM.c

 Description
   This file implements the decode Morse state machine.

****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this state machine as well as any machines at the
   next lower level in the hierarchy that are sub-machines to this machine
*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "DecodeMorseSM.h"
#include <stdio.h>
#include <ME218_C32.h>
#include <string.h>
#include "GameSM.h"
#include "const.h"


/*----------------------------- Module Defines ----------------------------*/
  
// Longest length of Morse character Signal
#define MORSE_LENGTH 8

/*---------------------------- Module Functions ---------------------------*/

static void ClearMorseChar(void);
static char DecodeMorse(void);

/*---------------------------- Module Variables ---------------------------*/
static DecodeState CurrentState;

// with the introduction of Gen2, we need a module level Priority var as well
static uint8_t MyPriority;

static char MorseString[MORSE_LENGTH];

// String to search for
static char KnownString[] = {'N', 'A'};
static unsigned int KnownStringPosition = 0;

// Possible chars
static char legalChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890?.,:'-/()\"= !$&+;@_";

// Morse code corresponding to the legalChars
static char MorseCode[][MORSE_LENGTH] ={ ".-", "-...", "-.-.", "-..", ".", "..-.", "--.",
                      "....", "..", ".---", "-.-", ".-..", "--", "-.", "---",
                      ".--.", "--.-", ".-.", "...", "-", "..-", "...-",
                      ".--", "-..-", "-.--", "--..", ".----", "..---",
                      "...--", "....-", ".....", "-....", "--...", "---..",
                      "----.", "-----", "..--..", ".-.-.-", "--..--",
                      "---...", ".----.", "-....-", "-..-.", "-.--.-",
                      "-.--.-", ".-..-.", "-...-", "-.-.--", "...-..-",
                      ".-...", ".-.-.", "-.-.-.", ".--.-.", "..--.-"
                     };

// Index to track the which place next dot or dashed will be placed in MorseString[]
static int index = 0;

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitDecodeMorseSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, sets up the initial transition and does any
     other required initialization for this state machine.
     Clears the MorseString by calling ClearMorseChar().
****************************************************************************/
boolean InitDecodeMorseSM (uint8_t Priority)
{
    ES_Event ThisEvent;
    MyPriority = Priority;

    // Clear the Morse character
    ClearMorseChar();

    ThisEvent.EventType = ES_INIT;
    if (ES_PostToService(MyPriority, ThisEvent) == True)
    {
        return True;
    }
    else
    {
        return False;
    }
}

/****************************************************************************
 Function
     PostDecodeMorseSM

 Parameters
     ES_Event ThisEvent : the event to post to the queue

 Returns
     boolean, False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
****************************************************************************/
boolean PostDecodeMorseSM(ES_Event ThisEvent)
{
    return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunDecodeMorseSM

 Parameters
   ES_Event : the event to process

 Returns
   ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
   Implements the DecodeMorse State Machine
****************************************************************************/
ES_Event RunDecodeMorseSM(ES_Event ThisEvent)
{
    ES_Event ReturnEvent;
    ReturnEvent.EventType = ES_NO_EVENT; // assume no errors

    // If the index ever gets too big, clearing Morse character
    if (index >= MORSE_LENGTH)
    {
        index = 0;
        ClearMorseChar();
    }

    // If a dot was detected
    if (ThisEvent.EventType == DOT_DETECTED)
    {
        if (index < MORSE_LENGTH)
        {
            // Add dot to MorseString
            strcat(MorseString, ".");
            index++;
        }
    }
    // If a dash was detected
    else if (ThisEvent.EventType == DASH_DETECTED)
    {
        if (index < MORSE_LENGTH)
        {
            // Add dash to MorseString
            strcat(MorseString, "-");
            index++;
        }
    }
    // If a EOC was detected
    else if (ThisEvent.EventType == EOC_DETECTED)
    {
        // If we received a valid character
        if (DecodeMorse() != '#')
        {
            // If character matches character we're searching for
            if (DecodeMorse() == KnownString[KnownStringPosition])
            {
                KnownStringPosition++;

                // If we've found all the characters
                if (KnownStringPosition >= sizeof(KnownString)/sizeof(char))
                {
                    ES_Event ThisEvent;

                    // Start game
                    ThisEvent.EventType = START_GAME;
                    PostGameSM(ThisEvent);

                    // Reset the position of character we're searching for
                    KnownStringPosition = 0;
                }
            }
            else
            {
                // If character doesn't match
                // Begin search from beginning
                KnownStringPosition = 0;
            }

            ClearMorseChar();
        }
    }
    else if (ThisEvent.EventType == EOW_DETECTED)
    {
        if (DecodeMorse() != '#')
        {
            // character not needed, so clear character
            ClearMorseChar();
        }
    }
    
    return ReturnEvent;
}

/****************************************************************************
 Function
     QueryDecodeMorseSM

 Parameters
     Nothing

 Returns
     DecodeState_t The current state of the Template state machine

 Description
     returns the current state of the Template state machine
 Notes

****************************************************************************/
DecodeState QueryDecodeMorseSM (void)
{
   return(CurrentState);
}


/*---------------------MODULE FUNCTIONS-------------------------------------*/
/****************************************************************************
 Function
     ClearMorseChar

 Parameters
     Nothing

 Returns
     Nothing

 Description
     Incements through MorseString and sets each position equal to ' '.

****************************************************************************/
static void ClearMorseChar(void)
{
    index = 0;
    strcpy(MorseString, "");
}


/****************************************************************************
 Function
     DecodeMorse

 Parameters
     Nothing

 Returns
     char : the decoded ASCII character

 Description
     Function increments through MorseCode checking to see if MorseString equals
     any of the elements in MorseCode. If a match is found the function returns the
     corresponding character in legalChars. If no match is found '#' is returned.
****************************************************************************/
static char DecodeMorse(void)
{
    static int i;
    static unsigned int numchars = sizeof(legalChars)/sizeof(char);
    
    // Check each received dot and dash against known Morse character
    for (i = 0; i < numchars; i++)
    {
        if (strcmp(MorseString, MorseCode[i]) == 0)
        {
            return legalChars[i];
        }
    }

    return '#';
}

EventCheckers.c

// Event Checking functions

#include "ES_Configure.h"
#include "ES_General.h"
#include "ES_Events.h"
#include "ES_PostList.h"
#include "EventCheckers.h"
#include "ES_Timers.h"
#include "ADS12.h"
#include "const.h"
#include <stdio.h>

// This include will pull in all of the headers from the service modules
// providing the prototypes for all of the post functions
#include "ES_ServiceHeaders.h"

/****************************************************************************
 Function
     Check4BatID

 Parameters
     Nothing

 Returns
     boolean : whether an event was found

 Description
     Checks for a signal from the BATT ID
****************************************************************************/
boolean Check4BatID(void)
{

    static unsigned char LastPinState = 0;
    unsigned char CurrentPinState;
    boolean ReturnVal = False;
    
    // Get current pin state
    CurrentPinState = (BATT_ID_PORT & BATT_ID_PIN);

    // If current pin state is different than last state
    if ((CurrentPinState != LastPinState))
    {
        ES_Event ThisEvent;

        // If pin state is high
        if ((CurrentPinState & BATT_ID_PIN) != 0)
        {
            // Post RISING_EDGE to MorseElements
            ThisEvent.EventType = RISING_EDGE;
            ThisEvent.EventParam = ES_Timer_GetTime();
            PostMorseElementsSM(ThisEvent);

            ReturnVal = True;
        }
        // If pin state is low
        else
        {
            // Post FALLING_EDGE to MorseElements
            ThisEvent.EventType = FALLING_EDGE;
            ThisEvent.EventParam = ES_Timer_GetTime();
            PostMorseElementsSM(ThisEvent);

            ReturnVal = True;
        }
    }

    // Store current pin state as last state
    LastPinState = CurrentPinState;

    return ReturnVal;
}

/****************************************************************************
 Function
     Check4ButtonAndTwist

 Parameters
     Nothing

 Returns
     boolean : whether an event was found

 Description
     Checks for a signal from the button or doorknob
****************************************************************************/
boolean Check4ButtonAndTwist(void)
{
    static unsigned char LastButtonPinState = 0;
    static unsigned char LastTwistPinState = 1;
    unsigned char CurrentButtonPinState;
    unsigned char CurrentTwistPinState;
    boolean ReturnVal = False;

    // If button state machine state is READY2SAMPLE
    if (QueryButtonDebounceSM() == READY2SAMPLE)
    {
        // Post START_DEBOUNCE event to Button
        ES_Event ThisEvent;
        ThisEvent.EventType = START_DEBOUNCE;
        PostButtonDebounceSM(ThisEvent);
        ReturnVal = True;

        // Get current button pin state
        CurrentButtonPinState = BUTTON_PORT & BUTTON_PIN;
        // Get current twist pin state
        CurrentTwistPinState = TWIST_PORT & TWIST_PIN;

        // If current button state is different than last state
        if (CurrentButtonPinState != LastButtonPinState)
        {
            // If pin state is high
            if ((CurrentButtonPinState & BUTTON_PIN) != 0)
            {
                ES_Event ThisEvent;

                // Post BUTTON_DOWN event to Game
                ThisEvent.EventType = BUTTON_DOWN;
                PostGameSM(ThisEvent);
                ReturnVal = True;
            }
        }
        // If current twist state is different than last state
        else if (CurrentTwistPinState != LastTwistPinState)
        {
            // If pin state is high
            if ((CurrentTwistPinState & TWIST_PIN) != 0)
            {
                ES_Event ThisEvent;

                // Post KNOB_TWISTED event to Game
                ThisEvent.EventType = KNOB_TWISTED;
                PostGameSM(ThisEvent);
                ReturnVal = True;
            }
        }

        // Store current button pin state as last button state
        LastButtonPinState = CurrentButtonPinState;

        // Store current knob pin state as last knob state
        LastTwistPinState = CurrentTwistPinState;
    
    }
    // If not ready to sample button
    else
    {
        // Do nothing
    }

    return ReturnVal;
}

/****************************************************************************
 Function
     Check4Swipe

 Parameters
     Nothing

 Returns
     boolean : whether an event was found

 Description
     Checks for a signal from the swipe sensor
****************************************************************************/
boolean Check4Swipe(void)
{
    static unsigned char LastPinState = 0;
    unsigned char CurrentPinState;
    boolean ReturnVal = False;

    // Get current swipe pin state
    CurrentPinState = SWIPE_PORT & SWIPE_PIN;

    // If current reflection state is different than last state
    if (CurrentPinState != LastPinState)
    {
        // If pin state is high
        if ((CurrentPinState & SWIPE_PIN) != 0)
        {
            ES_Event ThisEvent;

            // Post SWIPE_DETECTED event to Game
            ThisEvent.EventType = SWIPE_DETECTED;
            PostGameSM(ThisEvent);
            ReturnVal = True;
        }
    }
    
    // Store current reflection state as current pin state
    LastPinState = CurrentPinState;

    return ReturnVal;
}

/****************************************************************************
 Function
     Check4Grip

 Parameters
     Nothing

 Returns
     boolean : whether an event was found

 Description
     Get the value of the grip (force) sensor
****************************************************************************/
boolean Check4Grip(void)
{
    static unsigned short LastGrip = 0;
    unsigned short CurrentGrip;
    static unsigned short tolerance = 10;
    boolean ReturnVal = False;

    // If grip is ready to be sampled
    if (QueryGripSM() == READY2SAMPLE_GRIP)
    {
        // Post START_DEBOUNCE event to Grip
        ES_Event ThisEvent;
        ThisEvent.EventType = START_DEBOUNCE;
        PostGripSM(ThisEvent);
        ReturnVal = True;

        // Get current grip value
        CurrentGrip = ADS12_ReadADPin(GRIP_PIN);

        // If current pressure is different than last pressure
        if ((CurrentGrip > tolerance) &&
            (((CurrentGrip - LastGrip) > tolerance) ||
             ((LastGrip - CurrentGrip) > tolerance)))
        {
            ES_Event ThisEvent;

            // Post GRIP_AMOUNT event to Game with amount as EventParam
            ThisEvent.EventType = GRIP_AMOUNT;
            ThisEvent.EventParam = CurrentGrip;
            PostGameSM(ThisEvent);
            ReturnVal = True;
        }

        // Store last grip amount as current grip amount
        LastGrip = CurrentGrip;
    }

    return ReturnVal;
}

/****************************************************************************
 Function
     Check4Message

 Parameters
     Nothing

 Returns
     boolean : whether an event was found

 Description
     Check for new message from the other C32
****************************************************************************/
boolean Check4Message(void)
{
    static unsigned char LastPinState = 0x0f;
    unsigned char CurrentPinState;
    boolean ReturnVal = False;

    // If we're the one sending the message
    if ((COMM1_IO_PORT & COMM1_PIN) != 0)
    {
        // Check acknowledge line to see if message was gotten
        // Pulled high when acknowledged
        if ((COMM_ACK_PORT & COMM_ACK_PIN) != 0)
        {
            ES_Event ThisEvent;

            // Set data pins back to inputs
            ThisEvent.EventType = MESSAGE_CLEAR;
            PostMessageC32SM(ThisEvent);
        }
        // Don't check data pins
        return True;
    }

    // Read all 4 pins as set as current state
    CurrentPinState = ((COMM1_PORT & COMM1_PIN) != 0) +
        2*((COMM2_PORT & COMM2_PIN) != 0) +
        4*((COMM3_PORT & COMM3_PIN) != 0) +
        8*((COMM4_PORT & COMM4_PIN) != 0);

    // If current binary number of pins is different than last number
    if (CurrentPinState != LastPinState)
    {
        // If number is 0000
        if (CurrentPinState == MESSAGE_START_GAME)
        {
            ES_Event ThisEvent;
            ThisEvent.EventType = START_GAME;

            // Post START_GAME event to Game
            PostGameSM(ThisEvent);

            // Set acknowledge line as output
            COMM_ACK_IO_PORT |= COMM_ACK_PIN;
            // Set acknowledge line high
            COMM_ACK_PORT |= COMM_ACK_PIN;

            ReturnVal = True;
        }
        // If number is 0001
        if (CurrentPinState == MESSAGE_NEXT_STAGE_BUTTON_NEXT)
        {
            ES_Event ThisEvent;
            ThisEvent.EventType = NEXT_STAGE_BUTTON_NEXT;

            // Post NEXT_STAGE_BUTTON_NEXT event to Game
            PostGameSM(ThisEvent);

            // Set acknowledge line as output
            COMM_ACK_IO_PORT |= COMM_ACK_PIN;
            // Set acknowledge line high
            COMM_ACK_PORT |= COMM_ACK_PIN;

            ReturnVal = True;
        }
        // If number is 0010
        if (CurrentPinState == MESSAGE_NEXT_STAGE_TWIST_NEXT)
        {
            ES_Event ThisEvent;
            ThisEvent.EventType = NEXT_STAGE_TWIST_NEXT;
            
            // Post NEXT_STAGE_TWIST_NEXT event to Game
            PostGameSM(ThisEvent);
            
            // Set acknowledge line as output
            COMM_ACK_IO_PORT |= COMM_ACK_PIN;
            // Set acknowledge line high
            COMM_ACK_PORT |= COMM_ACK_PIN;

            ReturnVal = True;
        }
        // If number is 0011
        if (CurrentPinState == MESSAGE_NEXT_STAGE_SWIPE_NEXT)
        {
            ES_Event ThisEvent;
            ThisEvent.EventType = NEXT_STAGE_SWIPE_NEXT;
            
            // Post NEXT_STAGE_SWIPE_NEXT event to Game
            PostGameSM(ThisEvent);
            
            // Set acknowledge line as output
            COMM_ACK_IO_PORT |= COMM_ACK_PIN;
            // Set acknowledge line high
            COMM_ACK_PORT |= COMM_ACK_PIN;
            
            ReturnVal = True;
        }
        // If number is 0101
        if (CurrentPinState == MESSAGE_ACTION_COMPLETED_BUTTON_NEXT)
        {
            ES_Event ThisEvent;
            ThisEvent.EventType = ACTION_COMPLETED_BUTTON_NEXT;

            // Post ACTION_COMPLETED_BUTTON_NEXT event to Game
            PostGameSM(ThisEvent);
            
            // Set acknowledge line as output
            COMM_ACK_IO_PORT |= COMM_ACK_PIN;
            // Set acknowledge line high
            COMM_ACK_PORT |= COMM_ACK_PIN;
            
            ReturnVal = True;
        }
        // If number is 0110
        if (CurrentPinState == MESSAGE_ACTION_COMPLETED_TWIST_NEXT)
        {
            ES_Event ThisEvent;
            ThisEvent.EventType = ACTION_COMPLETED_TWIST_NEXT;
            
            // Post ACTION_COMPLETED_TWIST_NEXT event to Game
            PostGameSM(ThisEvent);
            
            // Set acknowledge line as output
            COMM_ACK_IO_PORT |= COMM_ACK_PIN;
            // Set acknowledge line high
            COMM_ACK_PORT |= COMM_ACK_PIN;
            
            ReturnVal = True;
        }
        // If number is 0111
        if (CurrentPinState == MESSAGE_ACTION_COMPLETED_SWIPE_NEXT)
        {
            ES_Event ThisEvent;
            ThisEvent.EventType = ACTION_COMPLETED_SWIPE_NEXT;

            // Post ACTION_COMPLETED_SWIPE_NEXT event to Game
            PostGameSM(ThisEvent);
            
            // Set acknowledge line as output
            COMM_ACK_IO_PORT |= COMM_ACK_PIN;
            // Set acknowledge line high
            COMM_ACK_PORT |= COMM_ACK_PIN;
            
            ReturnVal = True;
        }
        // If number is 1000
        if (CurrentPinState == MESSAGE_GAME_OVER)
        {
            ES_Event ThisEvent;
            ThisEvent.EventType = GAME_OVER;
            
            // Post GAME_OVER event to Game
            PostGameSM(ThisEvent);
            
            // Set acknowledge line as output
            COMM_ACK_IO_PORT |= COMM_ACK_PIN;
            // Set acknowledge line high
            COMM_ACK_PORT |= COMM_ACK_PIN;
            
            ReturnVal = True;
        }
        // If number is 1001
        if (CurrentPinState == MESSAGE_LOCKDOWN)
        {
            ES_Event ThisEvent;
            ThisEvent.EventType = LOCKDOWN;

            // Post LOCKDOWN event to Game
            PostGameSM(ThisEvent);
            
            // Set acknowledge line as output
            COMM_ACK_IO_PORT |= COMM_ACK_PIN;
            // Set acknowledge line high
            COMM_ACK_PORT |= COMM_ACK_PIN;
            
            ReturnVal = True;
        }
        // If number is 1111
        if (CurrentPinState == MESSAGE_NO_EVENT)
        {
            // Set acknowledge line low
            COMM_ACK_PORT &= ~COMM_ACK_PIN;
            // Set acknowledge line as output
            COMM_ACK_IO_PORT &= ~COMM_ACK_PIN;

            ReturnVal = False;
        }
    }
    
    // Store last message pin state as current pin state
    LastPinState = CurrentPinState;
    
    return ReturnVal;
}

GameSM.c

/****************************************************************************
 Module
   GameSM.c

 Revision
   1.0

 Description
   This is a Game state machine under the 
   Gen2 Events and Services Framework.

****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this state machine as well as any machines at the
   next lower level in the hierarchy that are sub-machines to this machine
*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "stdio.h"
#include "GameSM.h"
#include "const.h"
#include "ES_Timers.h"
#include "MessageC32SM.h"
#include "ServoLib.h"
#include "Audio.h"
#include <stdio.h>

/*----------------------------- Module Defines ----------------------------*/

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/

static GameState_t selectNextAction(unsigned int stage);
static void lightActionLED(GameState_t);
static void lightWinLEDs(boolean state);
static void communicateToC32(GameState_t NextState, unsigned int transitionValue);
static void sendMessageToC32(unsigned int message);
static void playMusicTrack(GameState_t NextState);
static unsigned int calculateStage2ITime(unsigned int stage1Points);
static void addToPoints(unsigned char stage, unsigned char answer);
static void gripAddToPoints(unsigned int gripAmount);
static void performLockdown(void);
static void performStartStage1_ActionNext(GameState_t action);
static unsigned int determineMessage(GameState_t presentState, GameState_t futureState);
static void initLights(void);

/*---------------------------- Module Variables ---------------------------*/
// everybody needs a state variable, you may need others as well.
// type of state variable should match that of enum in header file
static GameState_t CurrentState;

// with the introduction of Gen2, we need a module level Priority var as well
static unsigned char MyPriority;

// Action time in stage two
static unsigned int Stage2IndividualTime;

// Total points earned by player
static unsigned int Points;

// Ticks of physical timer gone by
// Max should be 45 (using 1 second ticks)
static unsigned char Ticks;

//time1, time2 to work as random number generators
static unsigned int time1;
static unsigned int time2;


/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitGameSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, sets up the initial transition and does any
     other required initialization for this state machine
****************************************************************************/
boolean InitGameSM(uint8_t Priority)
{
    unsigned int i = 0;

    // Store priority number
    MyPriority = Priority;

    // Generate first random number
    time1 = ES_Timer_GetTime();

    // Initialize all lights
    initLights();
    // Turn off all action LEDs
    lightActionLED(Reset);

    // Set current state as WaitingForBattID
    CurrentState = WaitingForBattID;

    // Turn off win LEDs
    lightWinLEDs(OFF);

    // Initialize to no time elapsed
    Ticks = 0;
   
    // Set pins 6 and 7 as servo outputs for timer and score
    Servo12_Init("SSxxxxxx");
   
    // Initialize Time servo
    Servo12_SetPulseWidth(TIMER_PIN_NUMBER, ((unsigned int)1000*Ticks)/45 + 700);
   
    //Initialize Score Servo
    Servo12_SetPulseWidth(SCORE_PIN_NUMBER, 1000);
     
    return True;
}

/****************************************************************************
 Function
     PostGameSM

 Parameters
     ES_Event ThisEvent : the event to post to the queue

 Returns
     boolean, False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
****************************************************************************/
boolean PostGameSM(ES_Event ThisEvent)
{
    return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunGameSM

 Parameters
    ES_Event : the event to process

 Returns
    ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
    Runs the entire game given the action occurring
****************************************************************************/
ES_Event RunGameSM(ES_Event ThisEvent)
{
    ES_Event ReturnEvent;
    GameState_t NextState = CurrentState;
    ReturnEvent.EventType = ES_NO_EVENT;

    // Increment physical timer based on TOTAL_GAME_TIMER
    if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == TOTAL_GAME_TIMER)
    {
        static unsigned int msTicks = 0;

        // Send a tick to the audio module
        audioTick();

        // If we reached 1 second and we're not resetting or waiting for BATT ID
        if (++msTicks >= _1_SEC && CurrentState != WaitingForBattID && CurrentState != Reset)
        {
            // Increment time ticks
            Ticks += 1;
            // Reset sub-second tick
            msTicks = 0;

            // Move servo of physical timer
            Servo12_SetPulseWidth(TIMER_PIN_NUMBER, ((unsigned int)1000*Ticks)/45 + 700);
        }

        // Reset timer
        ES_Timer_InitTimer(TOTAL_GAME_TIMER, 1);
    }

    /*----------------------WaitingForBattID------------------*/
    // If current state is WaitingForBattID
    if (CurrentState == WaitingForBattID)
    {
        // If event type is START_GAME
        if (ThisEvent.EventType == START_GAME)
        {
            // Select next action
            // Set current state as Stage1xx based on action chosen
            NextState = selectNextAction(STAGE1);

            // Present NEXT_STAGE_xx_NEXT to other C32
            sendMessageToC32(determineMessage(CurrentState, NextState));

            // Turn on corresponding action LED
            lightActionLED(NextState);

            // Start first track of music
            playMusicTrack(NextState);

            // Start stage_1 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_1_TIME);
            // Start lockdown timer
            ES_Timer_InitTimer(LOCKDOWN, LOCKDOWN_TIME);
            // Start total_game timer
            ES_Timer_InitTimer(TOTAL_GAME_TIMER, 1);
        }
        // If event type is NEXT_STAGE_BUTTON_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_BUTTON_NEXT)
        {
            // Set current state as Stage1xx based on event type
            NextState = Stage1Button;

            performStartStage1_ActionNext(NextState);
        }
        // If event type is NEXT_STAGE_TWIST_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_TWIST_NEXT)
        {
            // Set current state as Stage1xx based on event type
            NextState = Stage1Twist;

            performStartStage1_ActionNext(NextState);
        }
        // If event type is NEXT_STAGE_SWIPE_NEXT
        if (ThisEvent.EventType == NEXT_STAGE_SWIPE_NEXT)
        {
            // Set current state as Stage1xx based on event type
            NextState = Stage1Swipe;

            performStartStage1_ActionNext(NextState);
        }

    }

    /*----------------------Stage1Button------------------*/
    // If current state is Stage1Button
    else if (CurrentState == Stage1Button)
    {
        // If event type is GAME_OVER
        if (ThisEvent.EventType == GAME_OVER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(NextState);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);

        }
        // If event type is LOCK_DOWN
        else if (ThisEvent.EventType == LOCKDOWN)
        {
            // Set current state as RESET
            NextState = Reset;

            performLockdown();
        }
        // If event type is ES_TIMEOUT and parameter is lockdown timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == LOCKDOWN_TIMER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(NextState);

            // Present LOCKDOWN to other C32
            sendMessageToC32(MESSAGE_LOCKDOWN);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
        }
        // If event type is ES_TIMEOUT and parameter is stage_1 timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == STAGE_TIMER)
        {
            // Select next action
            // Set current state as Stage2xx based on action chosen
            NextState = selectNextAction(STAGE2);

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);

            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);

            // Present NEXT_STAGE_xx to other C32
            sendMessageToC32(determineMessage(CurrentState, NextState));

            // Select next track of music
            playMusicTrack(NextState);

            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);

            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        /*----Message from other C32 to go to next stage---*/
        // If event type is NEXT_STAGE_BUTTON_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_BUTTON_NEXT)
        {
            // Set current state as Stage2xx based on event
            NextState = Stage2Button;
            
            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);

            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);
            
            // Select next track of music
            playMusicTrack(NextState);
            
            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);

            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        // If event type is NEXT_STAGE_TWIST_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_TWIST_NEXT)
        {
            // Set current state as Stage2xx based on event
            NextState = Stage2Twist;

            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);
            
            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);
            
            // Select next track of music
            playMusicTrack(NextState);
            
            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);

            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        // If event type is NEXT_STAGE_SWIPE_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_SWIPE_NEXT)
        {
            // Set current state as Stage2xx based on event
            NextState = Stage2Swipe;

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);
            
            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);

            // Select next track of music
            playMusicTrack(NextState);
            
            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);
            
            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        /*----Action Completed by other C32 but staying in stage 1------*/
        // If event type is ACTION_COMPLETED_BUTTON_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_BUTTON_NEXT)
        {
            // Set current state as Stage1xx based on action chosen
            NextState = Stage1Button;
            
            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);
        }
        // If event type is ACTION_COMPLETED_TWIST_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_TWIST_NEXT)
        {
            // Set current state as Stage1xx based on action chosen
            NextState = Stage1Twist;

            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);        
        }                
        // If event type is ACTION_COMPLETED_SWIPE_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_SWIPE_NEXT)
        {
            // Set current state as Stage1xx based on action chosen
            NextState = Stage1Swipe;
            
            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);        
        }
        /*----Action Completed but staying in stage 1------*/
        // If event type is correct action BUTTON_DOWN
        else if (ThisEvent.EventType == BUTTON_DOWN)
        {
            // Select next action
            // Set current state as Stage1xx based on action chosen
            NextState = selectNextAction(STAGE1);

            // Add points
            addToPoints(STAGE1, CORRECT);

            // If points >= maximum
            if (Points >= MAX_POINTS)
            {
                // Set current state as Reset
                NextState = Reset;
                
                // Turn on win LEDs
                lightWinLEDs(ON);

                // Start reset timer
                ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);

                // Turn off all action LEDs
                lightActionLED(Reset);

                // Present GAME_OVER to other C32
                sendMessageToC32(MESSAGE_GAME_OVER);

                // Play end_of_game music
                playMusicTrack(Reset);
            }
            else
            {
                // Turn off all action LEDs
                lightActionLED(Reset);
               
                // Turn on corresponding action LED
                lightActionLED(NextState);

                // Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                sendMessageToC32(determineMessage(CurrentState, NextState));

                // Reset lockdown timer
                ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
            }
        }
        // If event type is wrong type KNOB_TWISTED or SWIPE_DETECTED
        else if (ThisEvent.EventType == KNOB_TWISTED || ThisEvent.EventType == SWIPE_DETECTED)
        {
            // Subtract points
            addToPoints(STAGE1, INCORRECT);
            
            // Reset lockdown timer
            ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
        }
    }

    /*----------------------Stage1Twist------------------*/
    // If current state is Stage1Twist
    else if (CurrentState == Stage1Twist)
    {
        // If event type is GAME_OVER
        if (ThisEvent.EventType == GAME_OVER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(NextState);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
        }
        // If event type is LOCK_DOWN
        else if (ThisEvent.EventType == LOCKDOWN)
        {
            // Set current state as RESET
            NextState = Reset;

            performLockdown();
        }
        // If event type is ES_TIMEOUT and parameter is lockdown timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == LOCKDOWN_TIMER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(NextState);

            // Present GAME_OVER to other C32
            sendMessageToC32(MESSAGE_GAME_OVER);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
        }
        // If event type is ES_TIMEOUT and parameter is stage_1 timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == STAGE_TIMER)
        {
            // Select next action
            // Set current state as Stage2xx based on action chosen
            NextState = selectNextAction(STAGE2);

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);

            // Present NEXT_STAGE_xx to other C32
            sendMessageToC32(determineMessage(CurrentState, NextState));

            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);

            // Select next track of music
            playMusicTrack(NextState);

            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);

            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        /*----Message from other C32 to go to next stage---*/
        // If event type is NEXT_STAGE_BUTTON_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_BUTTON_NEXT)
        {
            // Set current state as Stage2xx based on event
            NextState = Stage2Button;

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);

            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);
            
            // Select next track of music
            playMusicTrack(NextState);

            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);
            
            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        // If event type is NEXT_STAGE_TWIST_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_TWIST_NEXT)
        {
            // Set current state as Stage2xx based on event
            NextState = Stage2Twist;
            
            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);
            
            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);
            
            // Select next track of music
            playMusicTrack(NextState);

            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);

            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }

        // If event type is NEXT_STAGE_SWIPE_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_SWIPE_NEXT)
        {
            // Set current state as Stage2xx based on event
            NextState = Stage2Swipe;
            
            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);
            
            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);

            // Select next track of music
            playMusicTrack(NextState);
            
            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);
            
            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        /*----Action Completed by other C32 but staying in stage 1------*/
        // If event type is ACTION_COMPLETED_BUTTON_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_BUTTON_NEXT)
        {
            // Set current state as Stage1xx based on action chosen
            NextState = Stage1Button;

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);
        }
        // If event type is ACTION_COMPLETED_TWIST_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_TWIST_NEXT)
        {
            // Set current state as Stage1xx based on action chosen
            NextState = Stage1Twist;

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);
        }
        // If event type is ACTION_COMPLETED_SWIPE_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_SWIPE_NEXT)
        {
            // Set current state as Stage1xx based on action chosen
            NextState = Stage1Swipe;

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);
        }
        /*----Action Completed but staying in stage 1------*/
        // If event type is correct action :  KNOB_TWISTED
        else if (ThisEvent.EventType == KNOB_TWISTED)
        {
            // Select next action
            // Set current state as Stage1xx based on action chosen
            NextState = selectNextAction(STAGE1);

            // Add points
            addToPoints(STAGE1, CORRECT);

            // If points >= maximum
            if (Points >= MAX_POINTS)
            {
                // Set current state as Reset
                NextState = Reset;
                
                // Turn on win LEDs
                lightWinLEDs(ON);

                // Start reset timer
                ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);

                // Turn off all action LEDs
                lightActionLED(Reset);

                // Present GAME_OVER to other C32
                sendMessageToC32(MESSAGE_GAME_OVER);

                // Play end_of_game music
                playMusicTrack(Reset);
            }
            else
            {
                // Turn off all action LEDs
                lightActionLED(Reset);
               
                // Turn on corresponding action LED
                lightActionLED(NextState);

                // Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                sendMessageToC32(determineMessage(CurrentState, NextState));

                // Reset lockdown timer
                ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
            }
        }
        // If event type is wrong type BUTTON_DOWN or SWIPE_DETECTED
        else if (ThisEvent.EventType == BUTTON_DOWN || ThisEvent.EventType == SWIPE_DETECTED)
        {
            // Subtract points
            addToPoints(STAGE1, INCORRECT);

            // Reset lockdown timer
            ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
        }
    }
    /*----------------------Stage1Swipe------------------*/
    // If current state is Stage1Swipe
    else if (CurrentState == Stage1Swipe)
    {
        // If event type is GAME_OVER
        if (ThisEvent.EventType == GAME_OVER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(NextState);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
                    
        }
        // If event type is LOCK_DOWN
        else if (ThisEvent.EventType == LOCKDOWN)
        {
            // Set current state as RESET
            NextState = Reset;

            performLockdown();
        }
        // If event type is ES_TIMEOUT and parameter is lockdown timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == LOCKDOWN_TIMER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(NextState);

            // Present GAME_OVER to other C32
            sendMessageToC32(MESSAGE_GAME_OVER);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
        }
        // If event type is ES_TIMEOUT and parameter is stage_1 timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == STAGE_TIMER)
        {
            // Select next action
            // Set current state as Stage2xx based on action chosen
            NextState = selectNextAction(STAGE2);

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);

            // Present NEXT_STAGE_xx to other C32
            sendMessageToC32(determineMessage(CurrentState, NextState));

            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);

            // Select next track of music
            playMusicTrack(NextState);

            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);

            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        /*----Message from other C32 to go to next stage---*/
        // If event type is NEXT_STAGE_BUTTON_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_BUTTON_NEXT)
        {
            // Set current state as Stage2xx based on event
            NextState = Stage2Button;
            
            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);
            
            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);
            
            // Select next track of music
            playMusicTrack(NextState);
            
            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);
            
            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        // If event type is NEXT_STAGE_TWIST_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_TWIST_NEXT)
        {
            // Set current state as Stage2xx based on event
            NextState = Stage2Twist;
            
            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);
            
            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);
            
            // Select next track of music
            playMusicTrack(NextState);
            
            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);
            
            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        // If event type is NEXT_STAGE_SWIPE_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_SWIPE_NEXT)
        {
            // Set current state as Stage2xx based on event
            NextState = Stage2Swipe;
            
            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);
            
            // Calculate individual_action timer time
            Stage2IndividualTime = calculateStage2ITime(Points);
            
            // Select next track of music
            playMusicTrack(NextState);
            
            // Start individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);
            
            // Start stage_2 timer
            ES_Timer_InitTimer(STAGE_TIMER, STAGE_2_TIME);
        }
        /*----Action Completed by other C32 but staying in stage 1------*/
        // If event type is ACTION_COMPLETED_BUTTON_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_BUTTON_NEXT)
        {
            // Set current state as Stage1xx based on action chosen
            NextState = Stage1Button;
            
            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);
        }
        // If event type is ACTION_COMPLETED_TWIST_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_TWIST_NEXT)
        {
            // Set current state as Stage1xx based on action chosen
            NextState = Stage1Twist;
            
            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED
            lightActionLED(NextState);
        }
        // If event type is ACTION_COMPLETED_SWIPE_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_SWIPE_NEXT)
        {
            // Set current state as Stage1xx based on action chosen
            NextState = Stage1Swipe;
            
            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED
            lightActionLED(NextState);
        }
        /*----Action Completed but staying in stage 1------*/
        // If event type is correct action :  SWIPE_DETECTED
        else if (ThisEvent.EventType == SWIPE_DETECTED)
        {
            // Select next action
            // Set current state as Stage1xx based on action chosen
            NextState = selectNextAction(STAGE1);

            // Add points
            addToPoints(STAGE1, CORRECT);

            // If points >= maximum
            if (Points >= MAX_POINTS)
            {
                // Set current state as Reset
                NextState = Reset;
                
                // Turn on win LEDs
                lightWinLEDs(ON);

                // Start reset timer
                ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);

                // Turn off all action LEDs
                lightActionLED(Reset);

                // Present GAME_OVER to other C32
                sendMessageToC32(MESSAGE_GAME_OVER);

                // Play end_of_game music
                playMusicTrack(Reset);
            }
            else
            {
                // Turn off all action LEDs
                lightActionLED(Reset);

                // Turn on corresponding action LED
                lightActionLED(NextState);

                // Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                sendMessageToC32(determineMessage(CurrentState, NextState));

                // Reset lockdown timer
                ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
            }
        }
        // If event type is wrong type :  BUTTON_DOWN or KNOB TWISTED
        else if (ThisEvent.EventType == BUTTON_DOWN || ThisEvent.EventType == KNOB_TWISTED)
        {
            // Subtract points
            addToPoints(STAGE1, INCORRECT);
            
            // Reset lockdown timer
            ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
        }
    }
    
    /*-----------------STAGE2 BUTTON-------------------------*/
    // If current state is Stage2Button
    else if (CurrentState == Stage2Button)
    {
        // If event type is ES_TIMEOUT and event parameter is stage_2 timer
        if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == STAGE_TIMER)
        {
            // Set current state as STAGE_3
            NextState = Stage3;

            // Turn off action LEDs
            lightActionLED(Reset);
            // Turn on corresponding action LED
            lightActionLED(NextState);

            // Select next track of music
            playMusicTrack(NextState);
        }
        // If event type is LOCK_DOWN
        else if (ThisEvent.EventType == LOCKDOWN)
        {
            // Set current state as RESET
            NextState = Reset;

            performLockdown();
        }
        // If event is GAME_OVER
        else if (ThisEvent.EventType == GAME_OVER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
            
            // Turn off all action LEDs
            lightActionLED(NextState);

            // Stop music
            playMusicTrack(NextState);
        }
        // If event type is ES_TIMEOUT and parameter is lockdown timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == LOCKDOWN_TIMER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Present GAME_OVER to other C32
            sendMessageToC32(MESSAGE_LOCKDOWN);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
        }
        // If event type is correct action : BUTTON_DOWN
        else if (ThisEvent.EventType == BUTTON_DOWN)
        {
            // Select next action
            // Set current state as Stage2xx based on action chosen
            NextState = selectNextAction(STAGE2);

            // Add points
            addToPoints(STAGE2, CORRECT);

            // If points >= maximum
            if (Points >= MAX_POINTS)
            {
                // Set current state as Reset
                NextState = Reset;
                
                // Turn on win LEDs
                lightWinLEDs(ON);

                // Start reset timer
                ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);

                // Turn off all action LEDs
                lightActionLED(Reset);

                // Present GAME_OVER to other C32
                sendMessageToC32(MESSAGE_GAME_OVER);

                // Play end_of_game music
                playMusicTrack(Reset);
            }
            else
            {
                // Turn off all action LEDs
                lightActionLED(Reset);
               
                // Turn on corresponding action LED
                lightActionLED(NextState);

                // Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                sendMessageToC32(determineMessage(CurrentState, NextState));

                // Reset lockdown timer
                ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
            }
        }
        // If event type is wrong type KNOB_TWISTED or SWIPE_DETECTED
        else if (ThisEvent.EventType == KNOB_TWISTED || ThisEvent.EventType == SWIPE_DETECTED)
        {
            // Subtract points
            addToPoints(STAGE2, INCORRECT);
            
            // Reset lockdown timer
            ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
        }
        // If event type is ES_TIMEOUT and event parameter is individual_action timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == INDIVIDUAL_TIMER)
        {
            // Select next action
            // Set current state as Stage2xx based on action chosen
            NextState = selectNextAction(STAGE2);

            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED for next action
            lightActionLED(NextState);
            
            // Reset individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);
        }
    }

    /*-----------------STAGE2 TWIST-------------------------*/
    // If current state is Stage2Twist
    else if (CurrentState == Stage2Twist)
    {
        // If event type is ES_TIMEOUT and event parameter is stage_2 timer
        if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == STAGE_TIMER)
        {
            // Set current state as STAGE_3
            NextState = Stage3;

            // Turn off action LEDs
            lightActionLED(Reset);
            // Turn on corresponding action LED
            lightActionLED(NextState);

            // Select next track of music
            playMusicTrack(NextState);
        }
        // If event is GAME_OVER
        else if (ThisEvent.EventType == GAME_OVER)
        {
            // Set current state as RESET
            NextState = Reset;
            
            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
            
            // Turn off all action LEDs
            lightActionLED(NextState);
            
            // Stop music
            playMusicTrack(NextState);
        }
        // If event type is LOCK_DOWN
        else if (ThisEvent.EventType == LOCKDOWN)
        {
            // Set current state as RESET
            NextState = Reset;

            performLockdown();
        }
        // If event type is ES_TIMEOUT and parameter is lockdown timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == LOCKDOWN_TIMER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Present GAME_OVER to other C32
            sendMessageToC32(MESSAGE_LOCKDOWN);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
        }
        // If event type is correct action : KNOB_TWISTED
        else if (ThisEvent.EventType == KNOB_TWISTED)
        {
            // Select next action
            // Set current state as Stage2xx based on action chosen
            NextState = selectNextAction(STAGE2);

            // Add points
            addToPoints(STAGE2, CORRECT);

            // If points >= maximum
            if (Points >= MAX_POINTS)
            {
                // Set current state as Reset
                NextState = Reset;
                
                // Turn on win LEDs
                lightWinLEDs(ON);

                // Start reset timer
                ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);

                // Turn off all action LEDs
                lightActionLED(Reset);

                // Present GAME_OVER to other C32
                sendMessageToC32(MESSAGE_GAME_OVER);

                // Play end_of_game music
                playMusicTrack(Reset);
            }
            else
            {
                // Turn off all action LEDs
                lightActionLED(Reset);
               
                // Turn on corresponding action LED
                lightActionLED(NextState);

                // Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                sendMessageToC32(determineMessage(CurrentState, NextState));

                // Reset lockdown timer
                ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
            }
        }
        // If event type is wrong type BUTTON_DOWN or SWIPE_DETECTED
        else if (ThisEvent.EventType == BUTTON_DOWN || ThisEvent.EventType == SWIPE_DETECTED)
        {
            // Subtract points
            addToPoints(STAGE2, INCORRECT);

            // Reset lockdown timer
            ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
        }
        // If event type is ES_TIMEOUT and event parameter is individual_action timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == INDIVIDUAL_TIMER)
        {
            // Select next action
            // Set current state as Stage2xx based on action chosen
            NextState = selectNextAction(STAGE2);

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Turn on corresponding action LED for next action
            lightActionLED(NextState);

            // Reset individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);
        }
    }

    /*-----------------STAGE2 SWIPE-------------------------*/
    // If current state is Stage2Swipe
    else if (CurrentState == Stage2Swipe)
    {
        // If event type is ES_TIMEOUT and event parameter is stage_2 timer
        if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == STAGE_TIMER)
        {
            // Set current state as STAGE_3
            NextState = Stage3;

            // Turn off action LEDs
            lightActionLED(Reset);
            // Turn on corresponding action LED
            lightActionLED(NextState);

            // Select next track of music
            playMusicTrack(NextState);
        }
        // If event is GAME_OVER
        else if (ThisEvent.EventType == GAME_OVER)
        {
            // Set current state as RESET
            NextState = Reset;
            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
            // Turn off all action LEDs
            lightActionLED(NextState);
            // Stop music
            playMusicTrack(NextState);
        }
        // If event type is LOCK_DOWN
        else if (ThisEvent.EventType == LOCKDOWN)
        {
            // Set current state as RESET
            NextState = Reset;

            performLockdown();
        }
        // If event type is ES_TIMEOUT and parameter is lockdown timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == LOCKDOWN_TIMER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(Reset);

            // Present GAME_OVER to other C32
            sendMessageToC32(MESSAGE_LOCKDOWN);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
        }
        // If event type is correct action : SWIPE_DETECTED
        else if (ThisEvent.EventType == SWIPE_DETECTED)
        {
            // Select next action
            // Set current state as Stage2xx based on action chosen
            NextState = selectNextAction(STAGE2);
            
            // Add points
            addToPoints(STAGE2, CORRECT);

            // If points >= maximum
            if (Points >= MAX_POINTS)
            {
                // Set current state as Reset
                NextState = Reset;
                
                // Turn on win LEDs
                lightWinLEDs(ON);

                // Start reset timer
                ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);

                // Turn off all action LEDs
                lightActionLED(Reset);

                // Present GAME_OVER to other C32
                sendMessageToC32(MESSAGE_GAME_OVER);

                // Play end_of_game music
                playMusicTrack(Reset);
            }
            else
            {
                // Turn off all action LEDs
                lightActionLED(Reset);

                // Turn on corresponding action LED
                lightActionLED(NextState);

                // Present ACTION_COMPLETED_xx_NEXT to other C32 based on action chosen
                sendMessageToC32(determineMessage(CurrentState, NextState));

                // Reset lockdown timer
                ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
            }
        }
        // If event type is wrong type BUTTON_DOWN or KNOB_TWISTED
        else if (ThisEvent.EventType == BUTTON_DOWN || ThisEvent.EventType == KNOB_TWISTED)
        {
            // Subtract points
            addToPoints(STAGE2, INCORRECT);
            
            // Reset lockdown timer
            ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
        }
        // If event type is ES_TIMEOUT and event parameter is individual_action timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == INDIVIDUAL_TIMER)
        {
            // Select next action
            // Set current state as Stage2xx based on action chosen
            NextState = selectNextAction(STAGE2);

            // Turn off all action LEDs
            lightActionLED(Reset);
            
            // Turn on corresponding action LED for next action
            lightActionLED(NextState);
            
            // Reset individual_action timer
            ES_Timer_InitTimer(INDIVIDUAL_TIMER, Stage2IndividualTime);
        }
    }

    /*-------------------STAGE 3 :  GRIP----------------------------------*/
    // If current state is STAGE_3
    else if (CurrentState == Stage3)
    {
        // If game is over based on exceeding 45 seconds
        if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == TOTAL_GAME_TIMER && Ticks >= 45)
        {
             Set current state as RESET
            NextState = Reset;
            
            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
            
            // Turn off all action LEDs
            lightActionLED(NextState);

            // Stop music
            playMusicTrack(NextState);
        }
        // If event type is ES_TIMEOUT and parameter is lockdown timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == LOCKDOWN_TIMER)
        {
            // Set current state as RESET
            NextState = Reset;

            // Turn off all action LEDs
            lightActionLED(NextState);

            // Present GAME_OVER to other C32
            sendMessageToC32(MESSAGE_LOCKDOWN);

            // Stop music
            playMusicTrack(NextState);

            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
        }
        // If event type is GAME_OVER
        else if (ThisEvent.EventType == GAME_OVER)
        {
            // Set current state as RESET
            NextState = Reset;
            // Turn off all action LEDs
            lightActionLED(NextState);
            // Stop music
            playMusicTrack(NextState);
            // Start reset timer
            ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
        }
        // If event type is LOCK_DOWN
        else if (ThisEvent.EventType == LOCKDOWN)
        {
            // Set current state as RESET
            NextState = Reset;

            performLockdown();
        }
        // If event type is GRIP_AMOUNT
        else if (ThisEvent.EventType == GRIP_AMOUNT)
        {
            // Add points based on grip amount
            gripAddToPoints(ThisEvent.EventParam);

            // If points >= maximum
            if (Points >= MAX_POINTS)
            {
                // Set current state as Reset
                NextState = Reset;

                // Turn on win LEDs
                lightWinLEDs(ON);

                // Start reset timer
                ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);

                // Turn off all action LEDs
                lightActionLED(Reset);

                // Present GAME_OVER to other C32
                sendMessageToC32(MESSAGE_GAME_OVER);

                // Play end_of_game music
                playMusicTrack(Reset);
            }
            // Reset lockdown timer
            ES_Timer_InitTimer(LOCKDOWN_TIMER, LOCKDOWN_TIME);
        }
    }
    
    /*-------------------RESET----------------------------------*/
    // If current state is Reset
    else if (CurrentState == Reset)
    {
        // If event type is ES_TIMEOUT and parameter is LED_flash timer
        if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == LED_FLASH_TIMER)
        {
            // If points >= maximum
            if (Points >= MAX_POINTS)
            {
                // Flip state on win LEDs
                if ((WIN_LED_PORT & WIN_LED_PIN) == 0)
                {
                    WIN_LED_PORT |= WIN_LED_PIN;
                }
                else
                {
                    WIN_LED_PORT &= ~WIN_LED_PIN;
                }
            }
        }
        // If event type is ES_TIMEOUT and parameter is reset timer
        else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == RESET_TIMER)
        {
            // Turn win LEDs off
            lightWinLEDs(OFF);

            // Reset score servo
            Servo12_SetPulseWidth(SCORE_PIN_NUMBER, 1000);

            // Set points to 0
            Points = 0;

            // Reset timer servo
            Servo12_SetPulseWidth(TIMER_PIN_NUMBER, 700);
            Ticks = 0;

            // Set current state to WAITING_FOR_BATID
            NextState = WaitingForBattID;

            // Reset music (stop music, reset music to first track)
            playMusicTrack(WaitingForBattID);

            // Stop TOTAL_GAME_TIMER timer
            ES_Timer_StopTimer(TOTAL_GAME_TIMER);
        }
    }
    
    // Set current state as new state
    CurrentState = NextState;

    return ReturnEvent;
}

/****************************************************************************
 Function
     QueryGameSM

 Parameters
     Nothing

 Returns
     GameState_t The current state of the Game state machine

 Description
     returns the current state of the Game state machine
****************************************************************************/
GameState_t QueryGameSM(void)
{
   return(CurrentState);
}

/*-------------------------------private functions-------------------------*/
/****************************************************************************
 Function
     selectNextAction

 Parameters
     unsigned int stage : the stage number the game is in

 Returns
     GameState_t :  a randomly generated state of the Game state machine

 Description
     Maps a random number to a GameState_t based on the
     stage number inputted then returns this GameState_t

****************************************************************************/
GameState_t selectNextAction (unsigned int stage)
{
    unsigned int actionNumber;

    // Generate random number
    time2 = ES_Timer_GetTime();

    // Map random number to action
    actionNumber = (time2-time1) % 3;

    // Switch based on action number
    switch (actionNumber)
    {
        // If action number is 0
        case 0:
        {
            // If stage is stage 1
            if (stage == STAGE1)
            {
                // Return stage 1 button
                return Stage1Button;
            }
            else if (stage == STAGE2)
            {
                // Return stage 2 button
                return Stage2Button;
            }
        }
        // If action number is 1
        case 1:
        {
            // If state is stage 1
            if (stage == STAGE1)
            {
                // Return stage 1 twist
                return Stage1Twist;
            }
            // If stage is stage 2
            else if (stage == STAGE2)
            {
                // Return stage 2 twist
                return Stage2Twist;
            }
        }
        // If action number is 2
        case 2:
        {
            // If stage is stage 1
            if (stage == STAGE1)
            {
                // Return stage 1 swipe
                return Stage1Swipe;
            }
            // If stage is stage 2
            else if (stage == STAGE2)
            {
                // Return stage 2 swipe
                return Stage2Swipe;
            }
        }
    }
}


/****************************************************************************
 Function
     initLights

 Parameters
     Nothing

 Returns
     Nothing

 Description
     Turns all light ports as outputs.
****************************************************************************/
void initLights()
{
    BUTTON_LED_IO_PORT |= BUTTON_LED_PIN;
    SWIPE_LED_IO_PORT |= SWIPE_LED_PIN;
    TWIST_LED_IO_PORT |= TWIST_LED_PIN;
    GRIP_LED_IO_PORT |= GRIP_LED_PIN;
    WIN_LED_IO_PORT |= WIN_LED_PIN;
}

/****************************************************************************
 Function
     lightActionLED

 Parameters
     GameState_t

 Returns
     Nothing

 Description
     For a particular action state, it lights up the corresponding LEDs
     associated with that action.
****************************************************************************/
void lightActionLED(GameState_t CurrentState)
{
    // Turn all LEDs off
    BUTTON_LED_PORT = BUTTON_LED_PORT & ~BUTTON_LED_PIN;
    TWIST_LED_PORT = TWIST_LED_PORT & ~TWIST_LED_PIN;
    SWIPE_LED_PORT = SWIPE_LED_PORT & ~SWIPE_LED_PIN;
    GRIP_LED_PORT = GRIP_LED_PORT & ~GRIP_LED_PIN;

    // If action is button
    if ((CurrentState == Stage1Button) || (CurrentState == Stage2Button))
    {
        // Turn on button LED
        BUTTON_LED_PORT = BUTTON_LED_PORT | BUTTON_LED_PIN;
    }
    // If action is twist
    else if ((CurrentState == Stage1Twist) || (CurrentState == Stage2Twist))
    {
        // Turn on twist LED
        TWIST_LED_PORT = TWIST_LED_PORT | TWIST_LED_PIN;
    }
    // If action is swipe
    else if ((CurrentState == Stage1Swipe) || (CurrentState == Stage2Swipe))
    {
        // Turn on swipe LED
        SWIPE_LED_PORT = SWIPE_LED_PORT | SWIPE_LED_PIN;
    }
    // If action is grip
    else if (CurrentState == Stage3)
    {
        // Turn on grip LED
        GRIP_LED_PORT = GRIP_LED_PORT | GRIP_LED_PIN;
    }
    // If going to the reset stage
    else if (CurrentState == Reset)
    {
        // Turn off all action LEDs
        BUTTON_LED_PORT = BUTTON_LED_PORT & ~BUTTON_LED_PIN;
        TWIST_LED_PORT = TWIST_LED_PORT & ~TWIST_LED_PIN;
        SWIPE_LED_PORT = SWIPE_LED_PORT & ~SWIPE_LED_PIN;
        GRIP_LED_PORT = GRIP_LED_PORT & ~GRIP_LED_PIN;
    }
}

/****************************************************************************
 Function
     lightWinLEDs

 Parameters
     boolean state : whether to turn LEDs on or off

 Returns
     Nothing

 Description
     Turn winning leds on if boolean input is true, off if boolean input is
     false.
****************************************************************************/
void lightWinLEDs(boolean state)
{
    // If state is true
    if (state == True)
    {
        // Turn on LEDs
        WIN_LED_PORT |= WIN_LED_PIN;
    }
    // If state is false
    else
    {
        // Turn off LEDs
        WIN_LED_PORT &= ~WIN_LED_PIN;
    }
}

/****************************************************************************
 Function
     sendMessageToC32

 Parameters
     unsigned int message : message to send to other C32

 Returns
     Nothing

 Description
     Function to send a message to the other C32
     Uses MessageC32SM state machine to send message
****************************************************************************/
void sendMessageToC32(unsigned int message)
{
    ES_Event ThisEvent;

    // Switch based on message to send
    switch (message)
    {
        // If message is to start game
        case MESSAGE_START_GAME:
        {
            // Post START_GAME event
            ThisEvent.EventType = START_GAME;
            PostMessageC32SM(ThisEvent);
            break;
        }
        // If message is to go to next stage with button action next
        case MESSAGE_NEXT_STAGE_BUTTON_NEXT:
        {
            // Post NEXT_STAGE_BUTTON_NEXT
            ThisEvent.EventType = NEXT_STAGE_BUTTON_NEXT;
            PostMessageC32SM(ThisEvent);
            break;
        }
        // If message is to go to next stage with twist next
        case MESSAGE_NEXT_STAGE_TWIST_NEXT:
        {
            // Post NEXT_STAGE_TWIST_NEXT
            ThisEvent.EventType = NEXT_STAGE_TWIST_NEXT;
            PostMessageC32SM(ThisEvent);
            break;
        }
        // If message is to go to next stage with swipe next
        case MESSAGE_NEXT_STAGE_SWIPE_NEXT:
        {
            // Post NEXT_STAGE_SWIPE_NEXT
            ThisEvent.EventType = NEXT_STAGE_SWIPE_NEXT;
            PostMessageC32SM(ThisEvent);
            break;
        }
        // If message is action completed with button next
        case MESSAGE_ACTION_COMPLETED_BUTTON_NEXT:
        {
            // Post ACTION_COMPLETED_BUTTON_NEXT
            ThisEvent.EventType = ACTION_COMPLETED_BUTTON_NEXT;
            PostMessageC32SM(ThisEvent);
            break;
        }
        // If message is action completed with twist next
        case MESSAGE_ACTION_COMPLETED_TWIST_NEXT:
        {
            // Post ACTION_COMPLETED_TWIST_NEXT
            ThisEvent.EventType = ACTION_COMPLETED_TWIST_NEXT;
            PostMessageC32SM(ThisEvent);
            break;
        }
        // If message is action completed with swipe next
        case MESSAGE_ACTION_COMPLETED_SWIPE_NEXT:
        {
            // Post ACTION_COMPLETED_SWIPE_NEXT
            ThisEvent.EventType = ACTION_COMPLETED_SWIPE_NEXT;
            PostMessageC32SM(ThisEvent);
            break;
        }
        // If message is game over
        case MESSAGE_GAME_OVER:
        {
            // Post GAME_OVER
            ThisEvent.EventType = GAME_OVER;
            PostMessageC32SM(ThisEvent);
            break;
        }
        // If message is lockdown
        case MESSAGE_LOCKDOWN:
        {
            // Post LOCKDOWN
            ThisEvent.EventType = LOCKDOWN;
            PostMessageC32SM(ThisEvent);
            break;
        }
    }
}


/****************************************************************************
 Function
     determineMessage

 Parameters
      GameState_t presentState : current state of the game
      GameState_t futureState : next state of the game

 Returns
     unsigned int : message to be sent

 Description
      Based on the present state and future state, function determines
      the message that needs to be sent to the other C32.
****************************************************************************/
unsigned int determineMessage(GameState_t presentState, GameState_t futureState)
{
    // If currently waiting for Batt ID messages
    if (presentState == WaitingForBattID)
    {
        // If going to Stage1Button
        if (futureState == Stage1Button)
        {
            // Return MESSAGE_NEXT_STAGE_BUTTON_NEXT
            return MESSAGE_NEXT_STAGE_BUTTON_NEXT;
        }
        // If going to Stage1Twist
        else if (futureState == Stage1Twist)
        {
            // Return MESSAGE_NEXT_STAGE_TWIST_NEXT
            return MESSAGE_NEXT_STAGE_TWIST_NEXT;
        }
        // If going to Stage1Swipe
        else if (futureState == Stage1Swipe)
        {
            // Return MESSAGE_NEXT_STAGE_SWIPE_NEXT
            return MESSAGE_NEXT_STAGE_SWIPE_NEXT;
        }
    }

    // If currently in stage 1
    else if (presentState == Stage1Button || presentState == Stage1Twist || presentState == Stage1Swipe)
    {
        // If going to Stage1Button
        if (futureState == Stage1Button)
        {
            // Return MESSAGE_ACTION_COMPLETED_BUTTON_NEXT
            return MESSAGE_ACTION_COMPLETED_BUTTON_NEXT;
        }
        // If going to Stage1Twist
        else if (futureState == Stage1Twist)
        {
            // Return MESSAGE_ACTION_COMPLETED_TWIST_NEXT
            return MESSAGE_ACTION_COMPLETED_TWIST_NEXT;
        }
        // If going to Stage1Swipe
        else if (futureState == Stage1Swipe)
        {
            // Return MESSAGE_ACTION_COMPLETED_SWIPE_NEXT
            return MESSAGE_ACTION_COMPLETED_SWIPE_NEXT;
        }
        // If going to Stage1Button
        else if (futureState == Stage2Button)
        {
            // Return MESSAGE_NEXT_STAGE_BUTTON_NEXT
            return MESSAGE_NEXT_STAGE_BUTTON_NEXT;
        }
        // If going to Stage1Twist
        else if (futureState == Stage2Twist)
        {
            // Return MESSAGE_NEXT_STAGE_TWIST_NEXT
            return MESSAGE_NEXT_STAGE_TWIST_NEXT;
        }
        // If going to Stage1Swipe
        else if (futureState == Stage2Swipe)
        {
            // Return MESSAGE_NEXT_STAGE_SWIPE_NEXT
            return MESSAGE_NEXT_STAGE_SWIPE_NEXT;
        }
    }
}

/****************************************************************************
 Function
     playMusicTrack

 Parameters
     GameState_t NextState

 Returns
     Nothing

 Description
     Function to select what audio command to send to audio module
****************************************************************************/
void playMusicTrack(GameState_t NextState)
{
    // Switch based on NextState
    switch (NextState)
    {
        // If state is WaitingForBattID
        case WaitingForBattID:
        {
            // Stop music
            sendAudioCommand(0xFFFF);
            
            break;
        }
        // If state is Stage1Button
        case Stage1Button:
        {
            // Select state 1 music ("0000.ad4")
            sendAudioCommand(0x0000);
            
            // Play music
            sendAudioCommand(0xFFFE);
            
            break;
        }
        // If state is Stage1Twist
        case Stage1Twist:
        {
            // Select state 1 music ("0000.ad4")
            sendAudioCommand(0x0000);
            
            // Play music
            sendAudioCommand(0xFFFE);
            
            break;
        }
        // If state is Stage1Swipe
        case Stage1Swipe:
        {
            // Select state 1 music ("0000.ad4")
            sendAudioCommand(0x0000);
            // Play music
            sendAudioCommand(0xFFFE);
            
            break;
        }
        // If state is Stage2Swipe
        case Stage2Button:
        {
            // Select stage 2 music ("0001.ad4")
            sendAudioCommand(0x0001);

            break;
        }
        // If state is Stage2Twist
        case Stage2Twist:
        {
            // Select stage 2 music ("0001.ad4")
            sendAudioCommand(0x0001);

            break;
        }
        // If state is Stage2Swipe
        case Stage2Swipe:
        {
            // Select stage 2 music ("0001.ad4")
            sendAudioCommand(0x0001);
            
            break;
        }
        // If state is Stage3
        case Stage3:
        {
            // Select stage 3 music ("0002.ad4")
            sendAudioCommand(0x0002);
            
            break;
        }
        // If state is Reset
        case Reset:
        {
            // Select end of game music ("0003.ad4")
            sendAudioCommand(0x0003);

            break;
        }
    }
}

/****************************************************************************
 Function
     calculateStage2Time

 Parameters
     unsigned int stage1Points : number of points earned in stage 1

 Returns
     unsigned int : time allowed for in stage 1 (in milliseconds)

 Description
     Function to calculate the amount of time the user is allowed for each
     action in stage 2 based on their points in stage 1.
****************************************************************************/
unsigned int calculateStage2ITime(unsigned int stage1Points)
{
    unsigned int time;
    unsigned int max_time_allowed_for_anyone = 2000; // in milliseconds

    // Time is scaled from max time as fraction of MAX_POINTS earned
    time = (unsigned int)
        (((long)max_time_allowed_for_anyone*(MAX_POINTS-stage1Points))
        / MAX_POINTS);

    return time;
}

/****************************************************************************
 Function
     addToPoints

 Parameters
     unsigned char stage : number of current stage
     unsigned char answer : whether the answer was correct or incorrect

 Returns
     Nothing

 Description
     Function to update Points depending on the stage number and whether
     or not the correct action was preformed.
****************************************************************************/
void addToPoints(unsigned char stage, unsigned char answer)
{
    static unsigned char Stage1Multiplier = 30;
    static unsigned char Stage2Multiplier = 60;
    static unsigned char WrongAnswerPoints = 20;

    // If answer was incorrect
    if (answer == INCORRECT)
    {
        // Set points to max of 0 and points - wrong answer points
        if (Points < WrongAnswerPoints)
        {
            Points = 0;
        }
        else
        {
            Points -= WrongAnswerPoints;
        }
    }
    // If answer was correct
    else
    {
        switch (stage)
        {
            // If stage is 1
            case 1:
            {
                // Add stage 1 points
                Points += Stage1Multiplier;
                break;
            }
            // If stage is 2
            case 2:
            {
                // Add stage 2 points
                Points += Stage2Multiplier;
                break;
            }
        }
    }
    // If ponts > max ponts
    if (Points > MAX_POINTS)
    {
        // Set points equal to max points
        Points = MAX_POINTS;
    }
    
    // Set score servo
    Servo12_SetPulseWidth(SCORE_PIN_NUMBER, Points + 1000);
}

/****************************************************************************
 Function
     gripAddToPoints

 Parameters
     unsigned int gripAmount : analog value of the force sensor

 Returns
     Nothing

 Description
     Function to update Points depending on how hard person is gripping
****************************************************************************/
void gripAddToPoints(unsigned int gripAmount)
{
    static unsigned char Stage3Multiplier = 1;
    static unsigned int GripMaxAmount = 20;

    // Points is fixed amount scaled by fraction of grip amount over max
    Points += Stage3Multiplier*gripAmount/GripMaxAmount;
    
    // If points > max points
    if (Points > MAX_POINTS)
    {
        // Set points as max points
        Points = MAX_POINTS;
    }
    
    // Set score servo
    Servo12_SetPulseWidth(SCORE_PIN_NUMBER, Points + 1000);
}

/****************************************************************************
 Function
     performLockdown

 Parameters
     Nothing

 Returns
     Nothing

 Description
     Function to perform all the Lockdown functions
****************************************************************************/
void performLockdown(void)
{
    // Turn off all action LEDs
    lightActionLED(Reset);

    // Stop music
    playMusicTrack(Reset);

    // Start reset timer
    ES_Timer_InitTimer(RESET_TIMER, RESET_TIME);
}

/****************************************************************************
 Function
     performStartStage1_ActionNext

 Parameters
     GameState_t action : next action to go to

 Returns
     Nothing

 Description
     Function to perform all the Next_Stage_Action_Next functions
****************************************************************************/
void performStartStage1_ActionNext(GameState_t action)
{
    // Turn on corresponding action LED
    lightActionLED(action);

    // Start first track of music
    playMusicTrack(action);
    
    // Start lockdown timer
    ES_Timer_InitTimer(LOCKDOWN, LOCKDOWN_TIME);
    
    // Start total_game timer
    ES_Timer_InitTimer(TOTAL_GAME_TIMER, 1);
}

GripSM.c

/****************************************************************************
 Module
   GripSM.c

 Description
   This is a template file for implementing flat state machines under the
   Gen2 Events and Services Framework.

****************************************************************************/
/*----------------------------- Include Files -----------------------------*/

#include "ES_Configure.h"
#include "ES_Framework.h"
#include "GripSM.h"
#include <ME218_C32.h>
#include "const.h"
#include <stdio.h>
#include "GameSM.h"

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/

/*---------------------------- Module Variables ---------------------------*/
// everybody needs a state variable, you may need others as well.
// type of state variable should match that of enum in header file
static GripState CurrentState;

// with the introduction of Gen2, we need a module level Priority var as well
static uint8_t MyPriority;

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitGripSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, sets up the initial transition
     Initializes portM to read a signal from the Grip

****************************************************************************/
boolean InitGripSM(uint8_t Priority)
{
    ES_Event ThisEvent;
    
    // Initialize the MyPriority variable with the passed in parameter.
    MyPriority = Priority;

    // Set CurrentState to be DEBOUNCING
    CurrentState = DEBOUNCING_GRIP;

    // Start debounce timer (timer posts to GripSM)
    ES_Timer_InitTimer(LED_FLASH_TIMER, WIN_LED_TIME);
}

/****************************************************************************
 Function
     PostGripSM

 Parameters
     ES_Event ThisEvent : the event to post to the queue

 Returns
     boolean, False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
****************************************************************************/
boolean PostGripSM(ES_Event ThisEvent)
{
    return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunGripSM

 Parameters
    ES_Event : the event to process

 Returns
    ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
    Implements the two state state machine for RunDebounce
    2 States: ready2sample and debouncing
****************************************************************************/
ES_Event RunGripSM(ES_Event ThisEvent)
{
    ES_Event ReturnEvent;
    ReturnEvent.EventType = ES_NO_EVENT; // assume no error
    
    // If event type is ES_TIMEOUT
    if (ThisEvent.EventType == ES_TIMEOUT)
    {
        // If the debouncing timer timed out
        if (ThisEvent.EventParam == LED_FLASH_TIMER)
        {
            // Set current state to ready to sample
            CurrentState = READY2SAMPLE_GRIP;

            // Also post event to GameSM
            PostGameSM(ThisEvent);
        }
    }
    // If EventType is START_DEBOUNCE
    else if (ThisEvent.EventType == START_DEBOUNCE)
    {
        // Start timer (timer posts to GripSM)
        ES_Timer_InitTimer(LED_FLASH_TIMER, WIN_LED_TIME);

        // Set current state to DEBOUNCING
        CurrentState = DEBOUNCING_GRIP;
    }

    return ReturnEvent;
}

/****************************************************************************
 Function
     QueryGripSM

 Parameters
     Nothing

 Returns
     GripState : The current state of the Grip state machine

 Description
     returns the current state of GripSM

****************************************************************************/
GripState QueryGripSM (void)
{
   return(CurrentState);
}

MessageC32SM.c

/****************************************************************************
 Module
   MessageC32SM.c

 Description
   This is a template file for implementing flat state machines under the 
   Gen2 Events and Services Framework.

****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this state machine as well as any machines at the
   next lower level in the hierarchy that are sub-machines to this machine
*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "MessageC32SM.h"
#include "const.h"
#include <stdio.h>

/*----------------------------- Module Defines ----------------------------*/

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/

static void turnCOMMPortsInputs(void);
static void turnCOMMPortsOutputs(void);

/*---------------------------- Module Variables ---------------------------*/
// everybody needs a state variable, you may need others as well.
// type of state variable should match that of enum in header file
static MessageC32State_t CurrentState;

// with the introduction of Gen2, we need a module level Priority var as well
static uint8_t MyPriority;

// Last message sent (or attempted to send)
static unsigned char LastMessageSent;


/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitMessageC32SM

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, sets up the initial transition and does any
     other required initialization for this state machine
     Initializes all the comm ports as inputs.
****************************************************************************/
boolean InitMessageC32SM(uint8_t Priority)
{
    MyPriority = Priority;

    // Initialize all comm ports as inputs
    turnCOMMPortsInputs();

    // Set the current state to GettingMessage
    CurrentState = GettingMessage;

    return True;
}

/****************************************************************************
 Function
     PosMessagec32SM

 Parameters
     ES_Event ThisEvent : the event to post to the queue

 Returns
     boolean, False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
****************************************************************************/
boolean PostMessageC32SM(ES_Event ThisEvent)
{
    return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunMessageC32SM

 Parameters
    ES_Event : the event to process

 Returns
    ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
    Implements 2 State State machine for messaging the other C32.
****************************************************************************/
ES_Event RunMessageC32SM(ES_Event ThisEvent)
{
    ES_Event ReturnEvent;
    MessageC32State_t NextState;

    ReturnEvent.EventType = ES_NO_EVENT; // assume no errors

    // Set default next state as current state
    NextState = CurrentState;
    
    // If event type is ES_NEW_KEY
    if (ThisEvent.EventType == ES_NEW_KEY)
    {
        // Don't do anything
        return ReturnEvent;
    }

    // If waiting for incoming message
    if (CurrentState == GettingMessage)
    {
        // If message timer times out
        if ((ThisEvent.EventType == ES_TIMEOUT) &&
            (ThisEvent.EventParam == MESSAGE_TIMER))
        {
            // Repost last message event
            ES_Event ThisEvent2;
            ThisEvent2.EventType = LastMessageSent;
            ES_PostToService(MyPriority, ThisEvent2);
            return ReturnEvent;
        }
        
        // If event type is START_GAME
        if (ThisEvent.EventType == START_GAME)
        {
            // Set comm ports as outputs
            turnCOMMPortsOutputs();

            //Set 0x00
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
                (~COMM1_PIN & ~COMM2_PIN & ~COMM3_PIN & ~COMM4_PIN);

            // Save last message sent
            LastMessageSent = START_GAME;
        }
        // If event type is START_GAME
        else if (ThisEvent.EventType == NEXT_STAGE_BUTTON_NEXT)
        {
            // Set comm ports as outputs
            turnCOMMPortsOutputs();

            //Set 0x01
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
                (~COMM2_PIN & ~COMM3_PIN & ~COMM4_PIN);

            // Save last message sent
            LastMessageSent = NEXT_STAGE_BUTTON_NEXT;
        }
        // If event type is NEXT_STAGE_TWIST_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_TWIST_NEXT)
        {
            // Set comm ports as outputs
            turnCOMMPortsOutputs();

            //Set 0x02
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
                (~COMM1_PIN & ~COMM3_PIN & ~COMM4_PIN);

            // Save last message sent
            LastMessageSent = NEXT_STAGE_TWIST_NEXT;
        }
        // If event type is NEXT_STAGE_SWIPE_NEXT
        else if (ThisEvent.EventType == NEXT_STAGE_SWIPE_NEXT)
        {
            // Set comm ports as outputs
            turnCOMMPortsOutputs();

            //Set 0x03
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
                (~COMM3_PIN & ~COMM4_PIN);

            // Save last message sent
            LastMessageSent = NEXT_STAGE_SWIPE_NEXT;
        }
        // If event type is ACTION_COMPLETED_BUTTON_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_BUTTON_NEXT)
        {
            // Set comm ports as outputs
            turnCOMMPortsOutputs();

            //Set 0x05
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
                (~COMM2_PIN & ~COMM4_PIN);
            
            // Save last message sent
            LastMessageSent = ACTION_COMPLETED_BUTTON_NEXT;
        }
        // If event type is ACTION_COMPLETED_TWIST_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_TWIST_NEXT)
        {
            // Set comm ports as outputs
            turnCOMMPortsOutputs();

            //Set 0x06
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
                (~COMM1_PIN & ~COMM4_PIN);

            // Save last message sent
            LastMessageSent = ACTION_COMPLETED_TWIST_NEXT;
        }
        // If event type is ACTION_COMPLETED_SWIPE_NEXT
        else if (ThisEvent.EventType == ACTION_COMPLETED_SWIPE_NEXT)
        {
            // Set comm ports as outputs
            turnCOMMPortsOutputs();

            //Set 0x07
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
                (~COMM4_PIN);

            // Save last message sent
            LastMessageSent = ACTION_COMPLETED_SWIPE_NEXT;
        }
        // If event type is GAME_OVER
        else if (ThisEvent.EventType == GAME_OVER)
        {
            // Set comm ports as outputs
            turnCOMMPortsOutputs();

            //Set 0x08
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
                (~COMM1_PIN & ~COMM2_PIN & ~COMM3_PIN);
            
            // Save last message sent
            LastMessageSent = GAME_OVER;
        }
        // If event type is LOCKDOWN
        else if (ThisEvent.EventType == LOCKDOWN)
        {
            // Set comm ports as outputs
            turnCOMMPortsOutputs();

            //Set 0x09
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
                (~COMM2_PIN & ~COMM3_PIN);

            // Save last message sent
            LastMessageSent = LOCKDOWN;
        }
        // If event type is MESSAGE_CLEAR
        else if (ThisEvent.EventType == MESSAGE_CLEAR)
        {
            // Should not be here, so just return
            return ReturnEvent;
        }
        
        // Set next state as SendingMessage
        NextState = SendingMessage;
    }
    // If current state is SendingMessage
    else if (CurrentState == SendingMessage)
    {
        // If message timer times out
        if ((ThisEvent.EventType == ES_TIMEOUT) &&
            (ThisEvent.EventParam == MESSAGE_TIMER))
        {
            // Set comm ports as inputs
            turnCOMMPortsInputs();
            
            // Set next state as GettingMessage
            NextState = GettingMessage;
            
            // Start message timer
            ES_Timer_InitTimer(MESSAGE_TIMER, MESSAGE_TIME);
        }
        // If event type is MESSAGE_CLEAR
        else if (ThisEvent.EventType == MESSAGE_CLEAR)
        {
            // Set back to no event (1111) state on data pins
            PTT = (PTT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN);

            // Turn data pins back into inputs
            turnCOMMPortsInputs();

            // Set next state as GettingMessage
            NextState = GettingMessage;
        }
    }
    
    // Save next state as current state
    CurrentState = NextState;

    return ReturnEvent;
}

/****************************************************************************
 Function
     QueryMessageC32SM

 Parameters
     Nothing

 Returns
     MessageC32State_t : The current state of the Template state machine

 Description
     returns the current state of the MessageC32 state machine
****************************************************************************/
MessageC32State_t QueryMessageC32SM(void)
{
    return(CurrentState);
}

/*-----------------------private functions---------------------------------*/
/****************************************************************************
 Function
     turnCOMMPortsInputs

 Parameters
     Nothing

 Returns
     Nothing

 Description
     Makes all the COMM ports inputs
****************************************************************************/
void turnCOMMPortsInputs(void)
{
    DDRT = (DDRT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN) &
        (~COMM1_PIN & ~COMM2_PIN & ~COMM3_PIN & ~COMM4_PIN);
}

/****************************************************************************
 Function
     turnCOMMPortsOutputs

 Parameters
     Nothing

 Returns
     Nothing

 Description
     Makes all the COMM ports outputs
****************************************************************************/
void turnCOMMPortsOutputs(void)
{
    DDRT = (DDRT | COMM1_PIN | COMM2_PIN | COMM3_PIN | COMM4_PIN);
}

MorseElementsSM.c

/****************************************************************************
 Module
   MorseElementsSM.c

 Description
   This module implements the state machine for MorseElements.

****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "MorseElementsSM.h"
#include <stdio.h>
#include <ME218_C32.h>
#include "DecodeMorseSM.h"

/*----------------------------- Module Defines ----------------------------*/
#define DASH_DOT_RATIO 3
#define SPACE_DOT_RATIO 3
#define WORD_DOT_RATIO 7
#define PLUS_MINUS_DOT 8
#define PLUS_MINUS_DASH PLUS_MINUS_DOT*DASH_DOT_RATIO
#define PLUS_MINUS_SPACE PLUS_MINUS_DOT*SPACE_DOT_RATIO
#define PLUS_MINUS_WORD PLUS_MINUS_DOT*WORD_DOT_RATIO



/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/
   static void TestCalibration(void);
   static void characterizeSpace(void);
   static void characterizePulse(void);

/*---------------------------- Module Variables ---------------------------*/
static ElementState CurrentState;
static ElementState NextState;

static uint8_t MyPriority;
static unsigned int FirstDelta;
static unsigned int SecondDelta;
static unsigned int TimeOfLastFall;
static unsigned int TimeOfLastRise;
static unsigned int LengthOfDot;

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitMorseElementsSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, sets up the initial transition and does any 
     other required initialization for this state machine
****************************************************************************/
boolean InitMorseElementsSM(uint8_t Priority)
{
    ES_Event ThisEvent;

    MyPriority = Priority;

    // Put us into the Initial PseudoState
    CurrentState = InitMorseElements;

    FirstDelta = 0;

    // Post initial transition event
    ThisEvent.EventType = ES_INIT;
    if (ES_PostToService( MyPriority, ThisEvent) == True)
    {
        return True;
    }
    else
    {
        return False;
    }
}

/***********************************************************************
 Function
     PostMorseElementsSM

 Parameters
     ES_Event ThisEvent , the event to post to the queue

 Returns
     boolean, False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
****************************************************************************/
boolean PostMorseElementsSM( ES_Event ThisEvent)
{
    return ES_PostToService( MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunMorseElementsSM

 Parameters
    ES_Event : the event to process

 Returns
    ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
    Use switch statment to implement different actions based on different
    states and events

    Switch over states: InitMorseElements,CalWaitForRise,CalWaitForFall,
    EOC_WaitRise,EOC_WaitFall,DecodeWaitRise, DecodeWaitFall
****************************************************************************/
ES_Event RunMorseElementsSM(ES_Event ThisEvent)
{
    ES_Event ReturnEvent;
    ReturnEvent.EventType = ES_NO_EVENT; // assume no errors

    switch (CurrentState)
    {

        // InitMorseElements case: initializes the LCD display and sets the nextstate to CalWaitForRise
        case (InitMorseElements):
        {
            // If event type is ES_INIT
            if (ThisEvent.EventType == ES_INIT)
            {
                // Set next state to CalWaitForRise
                NextState = CalWaitForRise;
            }
            break;
        }
        case (CalWaitForRise):
        {
            // If event type is RISING_EDGE
            if (ThisEvent.EventType == RISING_EDGE)
            {
                // Save time of last rise
                TimeOfLastRise = ThisEvent.EventParam;
                // Set next state to CalWaitForFall
                NextState = CalWaitForFall;
            }
            // If event type is CALIBRATION_COMPLETE
            else if (ThisEvent.EventType == CALIBRATION_COMPLETE)
            {
                // Set next state to EOC_WaitRise
                NextState = EOC_WaitRise;
            }
            break;
        }
        case (CalWaitForFall):
        {
            // If event type is FALLING_EDGE
            if (ThisEvent.EventType == FALLING_EDGE)
            {
                // Save time of last fall
                TimeOfLastFall = ThisEvent.EventParam;
                // Set next state to CalWaitForRise
                NextState = CalWaitForRise;
                
                // Check calibration
                TestCalibration();
            }
            break;
        }
        case (EOC_WaitRise):
        {
            // If event type is RISING_EDGE
            if (ThisEvent.EventType == RISING_EDGE)
            {
                // Save time of last rise
                TimeOfLastRise = ThisEvent.EventParam;
                // Set next state to EOC_WaitFall
                NextState = EOC_WaitFall;

                // characterize space
                characterizeSpace();
            }
            break;
        }
        case (EOC_WaitFall):
        {
            // If event type is FALLING_EDGE
            if (ThisEvent.EventType == FALLING_EDGE)
            {
                // Save time of last fall
                TimeOfLastFall = ThisEvent.EventParam;
                // Set next state as EOC_WaitRise
                NextState = EOC_WaitRise;
            }
            else if (ThisEvent.EventType == EOC_DETECTED)
            {
                // Set next state as DecodeWaitFall
                NextState = DecodeWaitFall;
            }
            break;
        }
        case (DecodeWaitRise):
        {
            // If event type is RISING EDGE
            if (ThisEvent.EventType == RISING_EDGE)
            {
                // Save time of last rise
                TimeOfLastRise = ThisEvent.EventParam;
                // Set next state as DecodeWaitFall
                NextState = DecodeWaitFall;

                // characterize space
                characterizeSpace();
            }
            break;
        }
        case (DecodeWaitFall):
        {
            // If event type is FALLING_EDGE
            if (ThisEvent.EventType == FALLING_EDGE)
            {
                // Save time of last fall
                TimeOfLastFall = ThisEvent.EventParam;
                // Set next state as DecodeWaitRise
                NextState = DecodeWaitRise;
                
                // characterize pulse
                characterizePulse();
            }
            break;
        }
    }

    // Reset the current state
    CurrentState = NextState;

    return ReturnEvent;
}

/****************************************************************************
 Function
     QueryMorseElementsSM

 Parameters
     Nothing

 Returns
     ElementState : The current state of the MorseElements State Machine
****************************************************************************/
ElementState QueryMorseElementsSM(void)
{
   return(CurrentState);
}

/**************************PRIVATE FUNCTIONS*************************************************/

 /****************************************************************************
 Function
     TestCalibration

 Parameters
     Nothing

 Returns
     Nothing

 Description
   Calibrates the pulse signals and stores the calibrated length of a dot based
   on the incoming signal
****************************************************************************/
static void TestCalibration(void)
{
    ES_Event ThisEvent;
    
    // If calibration is just beginning
    if (FirstDelta == 0)
    {
        // Save first delta as difference in times of last fall and rise
        FirstDelta = TimeOfLastFall - TimeOfLastRise;
    }
    // Case where firstDelta is not zero (Calibration is in second stage)
    else
    {
        // Save second delta as difference in times of last fall and rise
        SecondDelta = TimeOfLastFall - TimeOfLastRise;
        
        // If first delta is about a third of second delta
        if ((100*FirstDelta/SecondDelta) <= 36)
        {
            // Post calibration complete event
            ThisEvent.EventType = CALIBRATION_COMPLETE;
            PostMorseElementsSM(ThisEvent);

            // Save length of dot as first delta
            LengthOfDot = FirstDelta;
        }
        // If first delta is about three times the length of second delta
        else if ((100*FirstDelta/SecondDelta) >= 279)
        {
            // Post calibration complete event
            ThisEvent.EventType = CALIBRATION_COMPLETE;
            PostMorseElementsSM(ThisEvent);
            
            // Save length of dot as second delta
            LengthOfDot = SecondDelta;
        }
        // If two deltas are too similar
        else
        {
            // Set first delta as second delta
            FirstDelta = SecondDelta;
        }
    }
}


/****************************************************************************
 Function
    characterizeSpace

 Parameters
     Nothing

 Returns
     Nothing

 Description
   Determines whether a voltage drop interval is an end of a character or a
   space between dots and dashes
****************************************************************************/
static void characterizeSpace(void)
{
    ES_Event ThisEvent;
    static unsigned int Lastinterval;
    
    // Set last interval as difference in time of last rise and fall
    Lastinterval = TimeOfLastRise - TimeOfLastFall;
    
    // If last interval was a character space
    if (Lastinterval >= (SPACE_DOT_RATIO*LengthOfDot - PLUS_MINUS_SPACE) &&
        Lastinterval <= (SPACE_DOT_RATIO*LengthOfDot + PLUS_MINUS_SPACE))
    {
        // Post EOC_DETECTED event
        ThisEvent.EventType = EOC_DETECTED;
        PostDecodeMorseSM(ThisEvent);
        PostMorseElementsSM(ThisEvent);
    }
    // If last interval was a word space
    else if (Lastinterval >= (WORD_DOT_RATIO*LengthOfDot - PLUS_MINUS_WORD) &&
        Lastinterval <= (WORD_DOT_RATIO*LengthOfDot + PLUS_MINUS_WORD))
    {
        // Post EOW_DETECTED event
        ThisEvent.EventType = EOW_DETECTED;
        PostDecodeMorseSM(ThisEvent);
   }
}


/****************************************************************************
 Function
    characterizePulse

 Parameters
    Nothing

 Returns
    Nothing

 Description
    Determines whether a voltage rise interval is a valid dot or dash
****************************************************************************/
static void characterizePulse(void)
{
    static unsigned int LastPulseWidth;
    ES_Event ThisEvent;
    
    // Set last interval as difference in time of last fall and rise
    LastPulseWidth = TimeOfLastFall - TimeOfLastRise;

    if (LastPulseWidth >= (LengthOfDot - PLUS_MINUS_DOT) &&
        LastPulseWidth <= (LengthOfDot + PLUS_MINUS_DOT))
    {
        // Post DOT_DETECTED event
        ThisEvent.EventType = DOT_DETECTED;
        PostDecodeMorseSM(ThisEvent);
    }
    else if (LastPulseWidth >= (DASH_DOT_RATIO*LengthOfDot - PLUS_MINUS_DASH) &&
        LastPulseWidth <= (DASH_DOT_RATIO*LengthOfDot + PLUS_MINUS_DASH))
    {
        // Post DASH_DETECTED event
        ThisEvent.EventType = DASH_DETECTED;
        PostDecodeMorseSM(ThisEvent);
    }
    else
    {
        ThisEvent.EventType = BAD_PULSE;
        PostDecodeMorseSM(ThisEvent);
    }
}

Main.c

#include <stdio.h>
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ES_Timers.h"
#include "const.h"
#include "ADS12.h"
#include "ServoLib.h"

void main(void) {
ES_Return_t ErrorType;

// Your hardware initialization function calls go here

// now initialize the Events and Services Framework and start it running
ErrorType = ES_Initialize(ES_Timer_RATE_1MS);

// Initialize the AD ports
// AD 7 is analog (analog input: grip input)
// AD 4,5,6 (inputs: audio busy, twist input, swipe input)
// AD 2,3 (outputs: audio data and audio clock)
if (ADS12_Init("AIIIOOOO") != ADS12_OK)
{
    // Error initializing AD pins
}

// Set action LEDs as outputs
BUTTON_LED_IO_PORT = BUTTON_LED_IO_PORT | BUTTON_PIN;
TWIST_LED_IO_PORT = TWIST_LED_IO_PORT | TWIST_PIN;
SWIPE_LED_IO_PORT = SWIPE_LED_IO_PORT | SWIPE_PIN;
GRIP_LED_IO_PORT = GRIP_LED_IO_PORT | GRIP_LED_PIN;

// Set win LED as output
WIN_LED_IO_PORT |= WIN_LED_PIN;

// Set button as input
BUTTON_IO_PORT &= ~BUTTON_PIN;

// Set BATT_ID as input
BATT_ID_IO_PORT &= ~BATT_ID_PIN;

if ( ErrorType == Success ) {

  ErrorType = ES_Run();

}

// If we got to here, there was an error
switch (ErrorType){
  case FailedPointer:
    puts("Failed on NULL pointer");
    break;
  case FailedInit:
    puts("Failed Initialization");
    break;
  default:
    puts("Other Failure");
    break;
}
for (;;)
  ;

};

/*------------------------------ End of file ------------------------------*/

Bill of Materials

Item Distributor Quantity Unit Unit Cost Total Cost
Mechanical Components and Sensors
2x4 wood framing Lowe's 1 12 ft. $7.97 $7.97
1/8" Duron paneling PRL 8 sq. ft. $0.75 $6.00
Krylon indoor/outdoor flat black spray paint BIC Warehouse 1 ea. $3.91 $3.91
Sheave pulley Ace 4 ea. $1.12 $4.48
Monofilament multi-purpose line Ace 10 yd. $0.02 $0.20
Hextronik HXT900 servo SPDL 1 ea. $3.60 $3.60
Vigor VTS-08B servo SPDL 2 ea. $6.15 $12.30
Vinyl cutout material Room 36 25 in. $0.10 $2.50
Wire for securing pulleys SPDL 1 ft. $0.10 $0.10
Wire/Cabling SPDL 52 ft. $0.10 $5.20
Kwikset hall and closet doorknob Mills Fleet Farm 1 pkg. $7.89 $7.89
Large dome push button Sparkfun 2 ea. $7.96 $15.92
Push button Sparkfun 1 ea. $0.95 $0.95
1.5" dowel Home Depot 2 ft. $1.40 $2.80
1/8" neoprene rubber McMaster-Carr 42 sq. in. $0.02 $0.84
BATT ID SPDL 1 ea. $0.00 $0.00
17 x 7/8" steel wire nails Ace 1 box $1.99 $1.99
Subtotal $76.65
Connectors and Fasteners
Pulley hook mount Ace 1 box $1.99 $1.99
3/4 x 1/2" corner brace Gridline supply 12 ea. $0.57 $6.84
10-24 x 3/8" machine screw McMaster-Carr 8 ea. $0.07 $0.56
10-24 nut McMaster-Carr 8 ea. $0.02 $0.16
8 x 3/8" sheet metal screw McMaster 4 ea. $0.07 $0.28
8 x 2" wood screw Ace 1 ea. $0.17 $0.17
8 x 1" wood screw McMaster-Carr 24 ea. $0.04 $0.96
6-32 x 2" machine screw McMaster-Car 4 ea. $0.06 $0.24
6-32 threading washer McMaster-Carr 4 ea. $0.02 $0.08
Corner bracket flat 2-1/2" The Hardware City 4 ea. $0.58 $2.32
1-12" corner brace The Hardware City 2 ea. $0.30 $0.60
10 x 1" sheet metal screw Ace 8 ea. $0.10 $0.80
3.5" 1/4-20 bolt McMaster-Carr 12 ea. $0.18 $2.16
1/4-20 nut McMaster-Car 12 ea. $0.02 $0.24
1/4-20 washer McMaster-Carr 24 ea. $0.02 $0.48
2-1/2" x 5/8" L-bracket The Hardware City 2 ea. $0.44 $0.88
1/4-20 x 2" bolt McMaster-Carr 8 ea. $0.08 $0.64
1/4-20 nut McMaster-Carr 8 ea. $0.02 $0.16
1/4-20 washer McMaster-Carr 16 ea. $0.02 $0.32
Subtotal $19.88
Electrical Components
Green LED SDL 6 ea. $0.25 $1.50
Yellow LED SPDL 4 ea. $0.25 $1.00
Red LED SPDL 4 ea. $0.25 $1.00
IR LED Lab kit 2 ea. $0.00 $0.00
220 uF electrolytic capacitor Lab kit 8 ea. $0.00 $0.00
220 uF electrolytic capacitor SPDL 3 ea. $0.10 $0.30
10 uF tantalum capacitor Lab kit 8 ea. $0.00 $0.00
10 uF tantalum capacitor SPDL 2 ea. $0.35 $0.70
100pF ceramic capacitor Lab kit 2 ea. $0.00 $0.00
N-channel MOSFET (2N7000) Lab kit 4 ea. $0.00 $0.00
N-channel MOSFET (2N7000) SPDL 5 ea. $0.10 $0.50
NPN Phototransistor (LTR-3208E) Lab kit 3 ea. $0.00 $0.00
N-channel power MOSFET (IRLZ34N) Lab kit 2 ea. $0.00 $0.00
100 ohm resistor Lab kit 8 ea. $0.00 $0.00
100 ohm resistor SPDL 4 ea. $0.01 $0.04
220 ohm resistor SPDL 1 ea. $0.01 $0.01
330 ohm resistor Lab kit 2 ea. $0.01 $0.02
470 ohm resistor Lab kit 1 ea. $0.00 $0.00
1k ohm resistor Lab kit 7 ea. $0.00 $0.00
2.2k ohm resistor Lab kit 1 ea. $0.00 $0.00
3.3k ohm resistor Lab kit 1 ea. $0.00 $0.00
3.9k ohm resistor Lab kit 2 ea. $0.00 $0.00
10k ohm resistor Lab kit 8 ea. $0.00 $0.00
10k ohm resistor SPDL 9 ea. $0.01 $0.09
47k ohm resistor Lab kit 2 ea. $0.00 $0.00
100k ohm resistor Lab kit 2 ea. $0.00 $0.00
240k ohm resistor SPDL 2 ea. $0.01 $0.02
1M ohm resistor SPDL 4 ea. $0.01 $0.04
Subtotal $5.22
Miscellaneous
Jumper wires for protoboards Lab kit 145 ea. $0.00 $0.00
Banana plugs Lab kit 4 ea. $0.00 $0.00
Alligator clips Lab kit 1 ea. $0.00 $0.00
Force sensitive resistors Robotshop.com 4 ea. $4.51 $18.04
13.8V/5V power supply SPDL 1 ea. $0.00 $0.00
Large breadboard Lab kit 2 ea. $0.00 $0.00
Small breadboard Sparkfun 1 ea. $3.00 $3.00
Freescale MC9S12C32 microcontroller SPDL 2 ea. $0.00 $0.00
Microcontroller power cable SPDL 2 ea. $0.00 $0.00
Large hinge lever limit switch SPDL 4 ea. $1.40 $5.60
Heat sink SPDL 1 ea. $0.35 $0.35
Perfboard Digikey 1 ea. $1.50 $1.50
2-position Molex connectors Digikey 20 ea. $0.09 $1.80
3-position Molex connectors Digikey 6 ea. $0.13 $0.78
4-position Molex connectors Digikey 1 ea. $0.16 $0.16
5-position Molex connectors Digikey 7 ea. $0.20 $1.40
+5V Voltage regulator SPDL 2 ea. $0.28 $0.56
Audio-sound module Sparkfun 1 ea. $21.56 $21.56
3.5mm audio jack Sparkfun 1 ea. $1.50 $1.50
Comparator (LM339N) Lab kit 3 ea. $0.00 $0.00
Op-amp (LM324) Lab kit 3 ea. $0.00 $0.00
14-pin DIP socket SPDL 4 ea. $0.40 $1.60
Subtotal $57.85
Grand Total
Total $159.60

Gems of Wisdom

  1. Simple, Simple, Simple:
    With such a difficult project, there is no need to make things any harder than they need to be. Build something that successfully meets the requirements and then build upon a working design if there is time remaining.
  2. Organization is the key to success:
    Keeping the listed items clean and organized will prevent many mistakes and save lots of time in the end.
    • Software: If the code is clearly commented and logically organized, it is much easier to read and therefore debug.
    • Circuits: Plan out how the circuits will be placed in your module frame. If this is not done, wires will be tangled, making circuit debugging a nightmare.
    • Components: Always buy spare components and organize them so that they are easily accessible.
    • Tools: The SPDL becomes a mess when the project is nearing its due date and finding the correct tool can take a frustratingly long amount of time. There is no point wasting this time each time you need something. Get your own set of whatever tools you need and keep them to yourself.
  3. Allocate resources efficiently:
    Break up the large tasks into smaller ones in order to use each person's time efficiently. i.e. Do not have four people debugging code at once.