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:- A player does not perform any actions for a particular period of time during stage 1 or stage 2.
- The time allotted for stage 3 expires.
- A player surpasses the maximum number of points during stage 1, stage 2, or stage 3. In this case the module will go into the reset stage and lights will blink on the side corresponding to the winner.
Demo Video
Game Specifications/Requirements
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.


Front View:


Side View:


Top View:


Base:


Grip:


Pulleys:

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
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.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.
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.
