#include <htc.h> #include "pic16f690.h" #include <stdio.h> #include "bitdefs.h" #include "ACVXbeePicUART.h" /*----------------- Module Definitions ------------------*/ typedef enum { Main_Idle = 0, //Team Finding States: Main_Listening = 1, Main_Confirming = 2, Main_Seeking = 3, Main_Timeout = 5, //Capturing Atoll States: Main_Capturing = 4, Main_Pending = 6, Main_Announcing = 7 } MainState_t; /*------------------ Module Functions -------------------*/ static void InitMain(void); static void InitTimer2(void); static void InitMotors(void); static void UpdateMainStateMachine(void); static void UpdateTimer2(void); static void UpdateMotors(void); static unsigned char IdentifyColor(unsigned char S1, unsigned char S2, unsigned char S3); static unsigned char IdentifyAtoll(unsigned char S1, unsigned char S2, unsigned char S3); /*------------------ Module Variables -------------------*/ //Message Flags static unsigned char NewXbeeMessageFlag = 0; static unsigned char NewSPIColorFlag = 0; static unsigned char NewSPIAtollFlag = 0; //Gameplay initialization static MainState_t MainState = Main_Idle; static unsigned char MyTeam = Team_None; static unsigned char MyFlag = Servo_None; //Transmission bytes static unsigned char Address_TeammateHigh; static unsigned char Address_TeammateLow; static unsigned char AtollNumber; static unsigned char CardColor; static unsigned char FrameID_Confirming = 0x01; static unsigned char FrameID_Seeking = 0x02; static unsigned char FrameID_Capturing = 0x03; static unsigned char FrameID_Announcing = 0x04; static unsigned char Atoll_S1; static unsigned char Atoll_S2; static unsigned char Atoll_S3; static unsigned char Atoll_SK1; static unsigned char Atoll_SK2; static unsigned char Atoll_SK3; //Main State Machine Timers and Timer Flags static unsigned char Timer_Listening; static unsigned char Timer_Confirming; static unsigned char Timer_Flags; static unsigned char Timer_Capturing; static unsigned char Timer_Pending; static unsigned char Timer_Announcing; static unsigned char TimerFlag_Listening = 0; static unsigned char TimerFlag_Confirming = 0; static unsigned char TimerFlag_Flags = 0; static unsigned char TimerFlag_Capturing = 0; static unsigned char TimerFlag_Pending = 0; static unsigned char TimerFlag_Announcing = 0; //Main state machine counter variables and flags static unsigned char Counter_Confirming = 0; static unsigned char Counter_Seeking = 0; static unsigned char Counter_Flags = 0; static unsigned char Counter_Capturing = 0; static unsigned char Counter_Pending = 0; static unsigned char Counter_Announcing = 0; static unsigned char Flag_Capturing = 0; static unsigned char Flag_Pending = 0; static unsigned char Flag_Announcing = 0; //Motor Transmission timeout static unsigned char Timer_Motors; static unsigned char TimerFlag_Motors = 1; /*-------------------- Module Code ----------------------*/ /*------------------- Init Functions --------------------*/ /********************************************************** Function: InitMain Parameters: none Returns: none Description: Clears Analog select registers, sets up debug ports **********************************************************/ static void InitMain(void){ //Clear ANSEL registers ANSEL = 0; ANSELH = 0; PORTC = 0b00010000; //state indicators TRISC0 = 0; TRISC1 = 0; TRISC2 = 0; TRISA2 = 0; } /********************************************************** Function: InitTimer2 Parameters: none Returns: none Description: Initializes timeout timer to 76Hz **********************************************************/ static void InitTimer2(void){ TMR2IF = 0; //reset timer flag TMR2 = 0; //reset timer PR2 = 255; //76Hz with 1:16 pre and postscale T2CON = 0b01111111; //T2 on with 1:16 pre and postscale } /********************************************************** Function: InitMotors Parameters: none Returns: none Description: Initializes motors to stopped states **********************************************************/ static void InitMotors(void){ CreateMotorTransmission(Servo_None, Motor_Stop, Motor_Stop); } /*------------------ Update Functions -------------------*/ /********************************************************** Function: UpdateTimer2 Parameters: none Returns: none Description: Updates all state timers on a new timer2 overflow. Sets flags if timers expire. **********************************************************/ static void UpdateTimer2(void){ if (TMR2IF == 1){ TMR2IF = 0; //Timer variable --; //If = 0, set flag Timer_Listening--; if (Timer_Listening == 0){ TimerFlag_Listening = 1; } Timer_Confirming--; if (Timer_Confirming == 0){ TimerFlag_Confirming = 1; } Timer_Flags--; if (Timer_Flags == 0){ TimerFlag_Flags = 1; } Timer_Capturing--; if (Timer_Capturing == 0){ TimerFlag_Capturing = 1; } Timer_Pending--; if (Timer_Pending == 0){ TimerFlag_Pending = 1; } Timer_Announcing--; if (Timer_Announcing == 0){ TimerFlag_Announcing = 1; } Timer_Motors--; if (Timer_Motors == 0){ TimerFlag_Motors = 1; } } } /********************************************************** Function: UpdateMotors Parameters: none Returns: none Description: Updates motors based on new transmission from CVC. If no transmission from CVC for 1 second, stops motors. **********************************************************/ static void UpdateMotors(void){ //If new message was received if (NewXbeeMessageFlag == 1){ //Check to see if it was a command from the CVC if ((ReadXbeeMessage(Byte_API) == API_Receive) && (ReadXbeeMessage(Byte_AddressHigh) == Address_CVCHigh) && (ReadXbeeMessage(Byte_AddressLow) == Address_CVCLow) && ((ReadXbeeMessage(Byte_MessageType) & MessageTypeMask) == MessageType_Direct) && ((ReadXbeeMessage(Byte_Command) == Command_C2B) || (ReadXbeeMessage(Byte_Command) == Start_Motor))){ //If we aren't during team setup if ((MainState != Main_Listening) && (MainState != Main_Confirming) && (MainState != Main_Seeking) && (MainState != Main_Timeout)){ //Add team flag signal to ServosByte unsigned char ServosByte; ServosByte = (ReadXbeeMessage(Byte_Servos) | MyFlag); //Update output port for god mode RC1 = ((ReadXbeeMessage(Byte_Servos) & BIT7HI) == BIT7HI); //Send data to motors CreateMotorTransmission(ServosByte, ReadXbeeMessage(Byte_Motor0), ReadXbeeMessage(Byte_Motor1)); //Reset Motor Timeout Timer_Motors = 76; TimerFlag_Motors = 0; } } } //If timed out, stop motors if ((TimerFlag_Motors == 1) && (MainState != Main_Timeout)){ //Reset Motor Timeout Timer_Motors = 76; TimerFlag_Motors = 0; //Kill god mode RC1 = 0; //Stop motors, with flag still raised. CreateMotorTransmission(MyFlag, Motor_Stop, Motor_Stop); } } /********************************************************** Function: UpdateMainStateMachine Parameters: none Returns: none Description: Updates team finding and atoll capturing state machine based on SPI and Xbee received messages. Event checking is all performed locally, within each state. **********************************************************/ static void UpdateMainStateMachine(void){ /* Overriding state transitions on color card scan */ if (NewSPIColorFlag == 1){ //Find Card Color CardColor = IdentifyColor(ReadSPIMessage(Byte_S1),ReadSPIMessage(Byte_S2),ReadSPIMessage(Byte_S3)); if (CardColor != Team_Error){ //Lower team flags MyFlag = Servo_None; CreateMotorTransmission(MyFlag, Motor_Stop, Motor_Stop); if (CardColor == Team_None){ //Clear team color MyTeam = Team_None; //set state to Idle MainState = Main_Idle; } else { //card color is red or green //Set Pending team color if (CardColor == Team_Red){ MyTeam = Team_RedPending; } else{ //CardColor == Team_Green) MyTeam = Team_GreenPending; } //set state to listening MainState = Main_Listening; //start 1s timer Timer_Listening = 76; TimerFlag_Listening = 0; } //Tell CVC new or pending color StartXbeeTransmission(FrameID_NoAck, Address_CVCHigh, Address_CVCLow); AddTransmissionData(Command_B2C); AddTransmissionData(MyTeam); FinishXbeeTransmission(); } } /* If there was no color scan, run normal state machine */ else{ switch (MainState){ /* State: Idle */ case Main_Idle: //If we scan an atoll card if (NewSPIAtollFlag == 1){ //Get Atoll data Atoll_S1 = ReadSPIMessage(Byte_S1); Atoll_S2 = ReadSPIMessage(Byte_S2); Atoll_S3 = ReadSPIMessage(Byte_S3); Atoll_SK1 = ReadSPIMessage(Byte_SK1); Atoll_SK2 = ReadSPIMessage(Byte_SK2); Atoll_SK3 = ReadSPIMessage(Byte_SK3); AtollNumber = IdentifyAtoll(Atoll_S1, Atoll_S2, Atoll_S3); if (AtollNumber != Atoll_Error){ //enter capturing state MainState = Main_Capturing; //Capturing state initialization Flag_Capturing = 1; //Set flag to send capture request Counter_Capturing = 0; //clear counter of number of capture requests Counter_Pending = 0; //clear counter of atoll responses FrameID_Capturing += 8; //Add 2^n so never hits 0. } } //If we happen to receive a random capture success message, why not use it: else if (NewXbeeMessageFlag == 1){ if ((ReadXbeeMessage(Byte_API) == API_Receive) && ((ReadXbeeMessage(Byte_MessageType) & MessageTypeMask) == MessageType_Direct) && (ReadXbeeMessage(Byte_Command) == Command_Reply) && (ReadXbeeMessage(Byte_Reply) == Reply_Success) && (ReadXbeeMessage(Byte_NewOwner) == MyTeam)){ //Set atoll to claim AtollNumber = ReadXbeeMessage(Byte_Atoll); //enter announcing state MainState = Main_Announcing; //Announcing State initialization Flag_Announcing = 1; //OK to send announcement Timer_Announcing = 76; //Set timer for broadcast success TimerFlag_Announcing = 0; //Clear timer flag Counter_Announcing = 0; //Clear counter of announcements FrameID_Announcing += 8; //Add 2^n so never hits 0. } } break; /* State: Listening for Teammate */ case Main_Listening: //If we hear from our teammate: if (NewXbeeMessageFlag == 1){ if ((ReadXbeeMessage(Byte_API) == API_Receive) && (ReadXbeeMessage(Byte_Command) == Command_FindTeam) && (ReadXbeeMessage(Byte_Team) == CardColor)){ //Store teammate address Address_TeammateHigh = ReadXbeeMessage(Byte_AddressHigh); Address_TeammateLow = ReadXbeeMessage(Byte_AddressLow); //enter confirming state MainState = Main_Confirming; //Confirming State initialization Counter_Confirming = 0; //Clear counter of confirmation attempts FrameID_Confirming += 8; //Add 2^n so never hits 0. } } //If we haven't heard from our teammate for 1 second, assume we are first boat else if (TimerFlag_Listening == 1){ //enter seeking state MainState = Main_Seeking; //Seeking state initialization Counter_Seeking = 0; //Clear counter of teammate finding broadcasts FrameID_Seeking += 8; //Add 2^n so never hits 0. } break; /* State: Confirming Team */ case Main_Confirming: // If we receive an ACK from our Xbee of our confirmation message if (NewXbeeMessageFlag == 1){ if ((ReadXbeeMessage(Byte_API) == API_Status) && (ReadXbeeMessage(Byte_FrameID) == FrameID_Confirming)){ if (ReadXbeeMessage(Byte_Status) == Status_Success){ //set team MyTeam = CardColor; if (CardColor == Team_Red){ MyFlag = Servo_Red; } else { MyFlag = Servo_Green; } //raise flag CreateMotorTransmission(MyFlag, Motor_Stop, Motor_Stop); //Tell CVC new color StartXbeeTransmission(FrameID_NoAck, Address_CVCHigh, Address_CVCLow); AddTransmissionData(Command_B2C); AddTransmissionData(MyTeam); FinishXbeeTransmission(); //return to idle MainState = Main_Idle; } } } // If we haven't received an ACK else if (CheckXbeeTransmitOK() == 1){ //Increase Counter of confirmation messages Counter_Confirming++; //If we have tried 10 times if (Counter_Confirming >= 10){ //go to timeout MainState = Main_Timeout; MyTeam = CardColor; if (CardColor == Team_Red){ MyFlag = Servo_Red; } else { MyFlag = Servo_Green; } //Timeout state initialization TimerFlag_Flags = 1; Counter_Flags = 0; } //If we haven't yet tried 10 times else { // send team confirmation StartXbeeTransmission(FrameID_Confirming, Address_TeammateHigh, Address_TeammateLow); AddTransmissionData(Command_FindTeam); AddTransmissionData(CardColor); FinishXbeeTransmission(); } } break; /* State: Seeking Teammate */ case Main_Seeking: //If reply received from teammate if (NewXbeeMessageFlag == 1){ if ((ReadXbeeMessage(Byte_API) == API_Receive) && (ReadXbeeMessage(Byte_Command) == Command_FindTeam) && (ReadXbeeMessage(Byte_Team) == CardColor)){ if ((ReadXbeeMessage(Byte_MessageType) & MessageTypeMask) == MessageType_Direct){ //set team MyTeam = CardColor; if (CardColor == Team_Red){ MyFlag = Servo_Red; } else { MyFlag = Servo_Green; } //raise flag CreateMotorTransmission(MyFlag, Motor_Stop, Motor_Stop); //Tell CVC new color StartXbeeTransmission(FrameID_NoAck, Address_CVCHigh, Address_CVCLow); AddTransmissionData(Command_B2C); AddTransmissionData(MyTeam); FinishXbeeTransmission(); //return to idle MainState = Main_Idle; } // If it wasn't a direct message else { //another team is broadcasting. what do we do? } } } //If we haven't received a reply from our teammate, broadcast again else if (CheckXbeeTransmitOK() == 1){ //Increase count of broadcasts Counter_Seeking++; //If we have broadcasted 255 times, if (Counter_Seeking >= 255){ //should be 255 //go to timeout MainState = Main_Timeout; MyTeam = CardColor; if (CardColor == Team_Red){ MyFlag = Servo_Red; } else { MyFlag = Servo_Green; } //Timeout state initialization TimerFlag_Flags = 1; Counter_Flags = 0; } //If we haven't yet broadcast 255 times else { //resend find teammate broadcast StartXbeeTransmission(FrameID_Seeking, Address_BroadcastHigh, Address_BroadcastLow); AddTransmissionData(Command_FindTeam); AddTransmissionData(CardColor); FinishXbeeTransmission(); } } break; /* State: Timing Out of Team Finding */ case Main_Timeout: //If timer has expired, telling us to switch flag that is showing if (TimerFlag_Flags == 1 ){ //Reset timer TimerFlag_Flags = 0; Timer_Flags = 38; //Increase count of flag flashings Counter_Flags ++; //If we have flashed our flags 20 times (10 seconds) if (Counter_Flags >= 20){ //raise flag CreateMotorTransmission(MyFlag, Motor_Stop, Motor_Stop); //Tell CVC new color StartXbeeTransmission(FrameID_NoAck, Address_CVCHigh, Address_CVCLow); AddTransmissionData(Command_B2C); AddTransmissionData(MyTeam); FinishXbeeTransmission(); //return to idle MainState = Main_Idle; } //If we haven't yet flashed our flags 20 times else { //Switch flags state CreateMotorTransmission(TimeoutServos[Counter_Flags % 2] , Motor_Stop, Motor_Stop); } } break; /* State: Broadcasting Capture Request */ case Main_Capturing: //If timer has expired (without an ACK) if (TimerFlag_Capturing == 1){ //Set flag to send new request Flag_Capturing = 1; } //If a transmit status is received for our capture request if (NewXbeeMessageFlag == 1){ if ((ReadXbeeMessage(Byte_API) == API_Status) && (ReadXbeeMessage(Byte_FrameID) == FrameID_Capturing)){ //if it was an ACK if (ReadXbeeMessage(Byte_Status) == Status_Success){ //enter pending state MainState = Main_Pending; //Pending State initialization Flag_Pending = 0; //Clear failure flag Timer_Pending = 38; //start state timer TimerFlag_Pending = 0; //clear state timer flag } //if it was not an ACK, message failed else { //Set flag to send new request Flag_Capturing = 1; } } } //If we are to request again else if ((Flag_Capturing == 1) && (CheckXbeeTransmitOK() == 1)){ //Reset Timers and Flags Flag_Capturing = 0; //Clear failure flag Timer_Capturing = 38; //Reset state timer TimerFlag_Capturing = 0; //Clear timeout flag //Increase capture request count Counter_Capturing++; //If we have requested a capture 3 times if (Counter_Capturing >= 3){ // return to idle MainState = Main_Idle; } //If we have yet to request a capture 3 times else { //resend capture request StartXbeeTransmission(FrameID_Capturing, Address_BroadcastHigh, Address_BroadcastLow); AddTransmissionData(Command_Capture); AddTransmissionData(MyTeam); AddTransmissionData(AtollNumber); AddTransmissionData(Atoll_S1); AddTransmissionData(Atoll_S2); AddTransmissionData(Atoll_S3); AddTransmissionData(Atoll_SK1); AddTransmissionData(Atoll_SK2); AddTransmissionData(Atoll_SK3); FinishXbeeTransmission(); } } break; /* State: Capture Request Pending */ case Main_Pending: //If timer has expired without a response if (TimerFlag_Pending == 1){ //Set flag to return to Capture Request state Flag_Pending = 1; } //If we have received a response from the Atoll if (NewXbeeMessageFlag == 1){ if ((ReadXbeeMessage(Byte_API) == API_Receive) && ((ReadXbeeMessage(Byte_MessageType) & MessageTypeMask) == MessageType_Direct) && (ReadXbeeMessage(Byte_Command) == Command_Reply)){ //If the message was for a successful capture if ((ReadXbeeMessage(Byte_Reply) == Reply_Success) && (ReadXbeeMessage(Byte_NewOwner) == MyTeam)){ //Set atoll to claim, since hey, we don't care if it's not the one we asked for AtollNumber = ReadXbeeMessage(Byte_Atoll); //enter announcing state MainState = Main_Announcing; //Announcing State initialization Flag_Announcing = 1; //OK to send announcement Timer_Announcing = 76; //Set timer for broadcast success TimerFlag_Announcing = 0; //Clear timer flag Counter_Announcing = 0; //Clear counter of announcements FrameID_Announcing += 8; //Add 2^n so never hits 0. } //If the message was not a successful capture else { //Set flag to return to capture request state Flag_Pending = 1; } } } //If we don't have a response but have timed out or failed else if ( Flag_Pending == 1 ){ //Increase count of overall capture attempts Counter_Pending ++; //If that was our second failure if (Counter_Pending >= 2){ //return to idle. do not pass go. do not collect $200. MainState = Main_Idle; } //If that was only our first failure else{ //reenter capturing state MainState = Main_Capturing; //Capturing state initialization Flag_Capturing = 1; //Set flag to send capture request Counter_Capturing = 0; //clear counter of number of capture requests FrameID_Capturing += 8; //Add 2^n so never hits 0. } } break; /* State: Announcing an Atoll Capture */ case Main_Announcing: //If we have a transmit status for our capture attempt if (NewXbeeMessageFlag == 1){ if ((ReadXbeeMessage(Byte_API) == API_Status) && (ReadXbeeMessage(Byte_FrameID) == FrameID_Announcing)){ if (ReadXbeeMessage(Byte_Status) == Status_Success){ //if successful broadcast, send a new one Flag_Announcing = 1; //Increase broadcast counter Counter_Announcing++; //Reset state timer Timer_Announcing = 76; TimerFlag_Announcing = 0; } } } //If state timer expires without transmit status if (TimerFlag_Announcing == 1){ //send new broadcast anyway Flag_Announcing = 1; //Increase broadcast counter Counter_Announcing++; //Reset state timer Timer_Announcing = 76; TimerFlag_Announcing = 0; } //If we are able to announce again if ((CheckXbeeTransmitOK() == 1) && (Flag_Announcing == 1)){ //Reset Flag Flag_Announcing = 0; //Reset state timer Timer_Announcing = 76; TimerFlag_Announcing = 0; //If we have already sent two announcements if (Counter_Announcing >= 2){ //return to idle, victorious MainState = Main_Idle; } //If we have not yet sent two announcements else { //send announcement StartXbeeTransmission(FrameID_Announcing, Address_BroadcastHigh, Address_BroadcastLow); AddTransmissionData(Command_Announce); AddTransmissionData(MyTeam); AddTransmissionData(AtollNumber); FinishXbeeTransmission(); } } break; } } } /*------------- Main and Helper Functions ---------------*/ /********************************************************** Function: Main Parameters: none Returns: none Description: Runs initialization and update functions. **********************************************************/ #ifndef UARTdebug void main(void){ //Run initialization functions InitMain(); InitTimer0(); InitUART(); InitSPI(); InitTimer2(); InitMotors(); //Main loop while(1){ //Run communication update functions UpdateUART(); UpdateSPI(); //update timeout timer UpdateTimer2(); //Check for new xbee message NewXbeeMessageFlag = 0; if (CheckReceiveMessageFlag() == 1){ NewXbeeMessageFlag = 1; } //Check for new card scan message NewSPIAtollFlag = 0; NewSPIColorFlag = 0; if (CheckSPIMessageFlag() > 0){ if (ReadSPIMessage(Byte_CardType) == Start_Atoll){ //If message was for an atoll scan NewSPIAtollFlag = 1; } else if (ReadSPIMessage(Byte_CardType) == Start_Color){ //if message was for a color scan NewSPIColorFlag = 1; } } //Run motor update function UpdateMotors(); //update team finding and atoll capture state machine UpdateMainStateMachine(); RC2 = (MainState > 1); //Debug output LED. Gives slight indicator of state. //Set debug output LEDs based on team state switch (MyTeam){ case Team_Green: RC0 = 0; RA2 = 1; break; case Team_Red: RC0 = 1; RA2 = 0; break; default: RC0 = 0; RA2 = 0; break; } } } #endif /********************************************************** Function: IdentifyColor Parameters: unsigned chars S1, S2, S3 (serial numbers) Returns: unsigned char - the team color of a scanned card, or Team_Error if none Description: Looks up the scanned color serial numbers in the ColorLookup array. Returns the matching color, if any. Otherwise returns Team_Error. **********************************************************/ static unsigned char IdentifyColor(unsigned char S1, unsigned char S2, unsigned char S3){ unsigned char i = 0; for (i = 0; i < 3; i++){ if ((S1 == ColorLookup[i][1]) && (S2 == ColorLookup[i][2]) && (S3 == ColorLookup[i][3])){ return ColorLookup[i][0]; //If we find the color, return it } } return Team_Error; } /********************************************************** Function: IdentifyAtoll Parameters: unsigned chars S1, S2, S3 (serial numbers) Returns: unsigned char - the atoll number of a scanned card, or Atoll_Error if none Description: Looks up the scanned color serial numbers in the AtollLookup array. Returns the matching atoll, if any. Otherwise returns Atoll_Error. **********************************************************/ static unsigned char IdentifyAtoll(unsigned char S1, unsigned char S2, unsigned char S3){ unsigned char i = 0; for (i = 0; i < 10; i++){ if ((S1 == AtollLookup[i][1]) && (S2 == AtollLookup[i][2]) && (S3 == AtollLookup[i][3])){ return AtollLookup[i][0]; //if we find the atoll, return it } } return Atoll_Error; }