Mbed based laser tag system using IR LEDs and receivers

Dependencies:   mbed fwdcrc16library PinDetect TextLCD

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 //////////////////////////////////////////
00002 //Embedded Systems Design Final Project //
00003 //Robert Michael Swan                   //
00004 //March 21, 2013                        //
00005 //                                      //
00006 //Compiled Code Size: 17.7KB            //
00007 //////////////////////////////////////////
00008 //Libraries Used
00009 #include "mbed.h"
00010 #include "TextLCD.h"
00011 #include "PinDetect.h"
00012 #include "crc16.h"
00013 //Peripherals 
00014 PinDetect  sw2(P0_11); //switch2 debounced input
00015 PinDetect  sw3(P0_10); //switch3 debounced input
00016 TextLCD lcd(P1_15, P1_16, P1_19, P1_20, P1_21, P1_22); // rs, e, d4-d7 led pins:4,6,11-14
00017 DigitalOut LB(P1_31); //lcd backlight
00018 DigitalOut LP(P1_29); //lcd power
00019 DigitalOut led1(P0_22); //on-board led ds1
00020 DigitalOut led2(P0_23); //on-board led ds2
00021 Serial IR(P1_27,P1_26); //IR transmitter and receiver output and input control P1_27 controls IR diode, P1_26 controls IR reciever
00022 PwmOut Square_Gen(P1_25);       //square wave generator for IR led
00023 //Timers
00024 Timeout stats;   //used for displaying text for limited periods of time
00025 Timeout shot_time; //used to ensure shots take place in a brief period of time
00026 Timeout flash;  //used to flash leds and backlight when shooting or tagging
00027 Timer timer;    //used as seconds for reset timer       
00028 Ticker clock_tick;    //used with timer for minutes of reset timer
00029 //Required global variables
00030 unsigned mins = 0;   //global variable for minutes in game, since ticker interrupt cannot attach functions that have parameters
00031 bool btn2 = false;  //global variable for sw2, since the PinDetect interrupt cannot attach functions that have parameters
00032 bool btn3 = false;  //global variable for sw3, since the PinDetect interrupt cannot attach functions that have parameters
00033 bool done = false;   //global variable for timeout, since TimeOut interrupt cannot attach functions that have parameters
00034 bool taggable = false;  //global variable for whether serial received input will be used or not
00035 volatile bool tagged = false;     //global variable for whether tag is complete
00036 char id_rx[15];         //global variable for received ID. Required to use serial interrupt correctly
00037 unsigned id_pos = 0;    //global variable for recieved ID array location. Also required for serial interrupt.
00038 
00039 //Functions
00040 /*Minute Interrupt
00041     Intended to be called every minute to increment the minute counter.
00042     Also resets the seconds timer as it reaches 60 seconds (assuming it is called at timer.start();)
00043 */
00044 void minutes()
00045 {
00046     mins ++;
00047     timer.reset();
00048 }
00049 
00050 /* Flashing interrupt
00051     Intended to be used with a timeout object to flash lights when shot or shooting
00052 */
00053 void flasher()
00054 {
00055     LB = 0; 
00056     led1 = !led1;
00057     led2 = !led2;
00058 }
00059 
00060 /*Shot Check/Reset Interrupt
00061     Intended to be called after a short period of time to start array over from the start
00062     Should keep data from being received over an excessive amount of time and thus is one
00063     method of ensuring player is actually tagged
00064 */
00065 void shot_check()
00066 {
00067     id_pos = 0;     //resets current id
00068 }
00069 
00070 /*Serial Callback interrupt
00071     Receives IR information using IR receiver
00072     If taggable = false, received data is thrown away.
00073     Otherwise, up to 15 characters are collected in a time period of 0.5 seconds
00074     If less than 15 characters received in 0.5 seconds, function array returns to first bit
00075 */
00076 void callback() 
00077 {
00078     // Note: you need to actually read from the serial buffer to clear the RX interrupt
00079     if  (!taggable)
00080     { 
00081         IR.getc(); //ensures no extra buffering left over when player is not taggable
00082         return; 
00083     }
00084     
00085     id_rx[id_pos] = IR.getc();  //place received character into array
00086     /*if (id_pos == 0)    //starts a timer for the time in which receiver must receive a full id
00087     {
00088         shot_time.attach(&shot_check, 0.5);
00089     }*/
00090     if(id_pos >= 14)   
00091     {
00092         id_pos = 0; //ensures no array overflows
00093         shot_time.detach(); //ensures no further action upon array until next time tagged
00094         tagged = true;  //changes tagged flag such that the main code will trigger after an ID is fully received
00095         return;
00096     }
00097     id_pos ++;  //increment to next array position for id
00098     return;    
00099 }
00100 
00101 /*Button 2 Interrupt
00102     Sets button variable to true for use in code when button is pressed
00103 */
00104 void btn2Pressed()
00105 {
00106     led1 = !led1;
00107     btn2 = true;
00108 }
00109 
00110 /*Button 3 Interrupt
00111     Sets button variable to true for use in code when button is pressed
00112 */
00113 void btn3Pressed()
00114 {
00115     led2 = !led2;
00116     btn3 = true;
00117     
00118 }
00119 
00120 /*Statistics Display Timeout
00121     Sets display "done" flag to true
00122     Intended to be used with timeout interrupt and statistics function to jump out of function
00123     if it has been 5 seconds.
00124 */
00125 void finish_disp ()
00126 {
00127     done = true;
00128     return;
00129 }
00130 
00131 /*Error Routine.
00132     Displays "ERROR" on LCD screen and flashes leds
00133     for an indefinite period of time.
00134 */
00135 void error_custom ()
00136 {
00137     lcd.cls();
00138     lcd.printf("ERROR");
00139     led1 = 0;
00140     led2 = 1;
00141     while(1)
00142     {
00143       led1 = !led1;
00144       led2 = !led2;
00145       wait(0.5);
00146     } 
00147 }
00148 /*Alphanumeric selector
00149     Used with char_sel function in order to select a number 0-9 or letters A-Z through user input.
00150     Returns a char.
00151 */
00152 char alpha_num_sel (bool number)
00153 {
00154     char n0 = '0';
00155     char n_tmp = '0';
00156     unsigned incr = 0;   //used to increment characters between 0-9 or A-Z 
00157     unsigned mod;
00158     if (number)
00159     {
00160         lcd.cls();
00161         lcd.printf("Pick\nNumber:%c",n_tmp);
00162         n0 = '0';
00163         n_tmp = n0;
00164         mod = 11;   //modulus for character range
00165     }
00166     else
00167     {
00168         lcd.cls();
00169         n0 = 'A';
00170         n_tmp = n0;
00171         lcd.printf("Pick\nLetter:%c",n_tmp);
00172         mod = 26;
00173     }
00174     while(1)
00175     {
00176         while(!btn2 && !btn3)   //waits until button input received
00177         {
00178             wait(0.05);
00179         }
00180         if(btn3)
00181         {
00182             btn3 = false;
00183             btn2 = false;       //if both butons pressed, it just selects the current character
00184             return (n_tmp);
00185         }
00186         else if(btn2)
00187         {
00188             btn2 = false;
00189             incr ++;
00190             incr = incr % mod; //ensures that numbers stay within the 0-9/A-Z range
00191             n_tmp = n0 + incr;    //increment current character selected
00192             if (incr == 10 && number) //feature addition to allow for space entry
00193             {
00194                 n_tmp = ' ';
00195             }
00196             lcd.cls();
00197             if(number)
00198             {
00199                 lcd.printf("Pick\nNumber:%c",n_tmp);    //displays current number
00200             }
00201             else
00202             {
00203                 lcd.printf("Pick\nLetter:%c",n_tmp);     //displays current letter
00204             }
00205         }
00206         else
00207         {
00208             error_custom();
00209         }
00210     
00211     }
00212 }
00213 
00214 /*Character Selection
00215     Used with setup() function in order to pick a character A-Z and number 0-9
00216     for use as part of user id and/or team. Passed a 0 if selecting team letter (A-Z),
00217     otherwise will select player id letter
00218 */
00219 char char_sel (bool player)
00220 {
00221     if (player)
00222     {
00223         lcd.cls();
00224         lcd.printf("A-Z? or\n0-9?");
00225         while(!btn2 && !btn3)   //waits until button input received
00226         {
00227             wait(0.05);
00228         }
00229         if(btn2)    //if select button pressed, goes through numerical selection
00230         {
00231             btn2 = false;
00232             btn3 = false;   //if both buttons pressed at the same time, it just does numerical selection
00233             
00234             return(alpha_num_sel(1));
00235         }
00236         else if(btn3)
00237         {
00238             btn3 = false;   //pressing the option button in this case is the same as selecting a team
00239         }
00240         else
00241         {
00242             error_custom(); //if you somehow make it here without pressing buttons, it will throw an error
00243         }    
00244     }
00245 return(alpha_num_sel(0)); //returns capital alpha character for id or team by calling selection function
00246 }
00247         
00248     
00249 /*Setup Routine.
00250     Sets the user id for use in-game.
00251     Function must be passed an array of characters of size 15
00252     in order to work correctly.
00253 */
00254 void setup (char* ID_array)
00255 {
00256     unsigned size = 1;
00257     
00258     lcd.cls();
00259     lcd.printf("ID Size?\n%u",size);
00260     
00261     while(1)
00262     {
00263         while(!btn2 && !btn3)   //waits until button input received
00264         {
00265             wait(0.05);     //ensures buttons cannot be pressed extremely fast
00266         }
00267         if(btn2 && btn3)    //routine to set size of player id in bytes
00268         {
00269             btn2 = false;   //handling for simultaneous button pressing
00270             btn3 = false;   
00271             size++;
00272             if (size > 12) 
00273             {
00274                 size = 1;    //ensures size cannot be greater than 12 or less than 1
00275             }
00276             break;          //sets size to whatever current size is +1 and moves on
00277         }
00278         else if(btn2)   //increases size or loops back to 1, displays current size
00279         {
00280             btn2 = false;
00281             size++;
00282             if (size > 12) 
00283             {
00284                 size = 1;   //ensures size cannot be greater than 12 or less than 1
00285             }
00286             lcd.cls();
00287             lcd.printf("ID Size?\n%u",size);    //displays current size
00288         }
00289         else if(btn3)
00290         {
00291             btn3 = false;   //selects current size
00292             break;
00293         }
00294         else
00295         {
00296             error_custom();    //error called if unexpected output
00297         }
00298     }
00299         
00300     for (unsigned j = 0; j < size; j ++)    //sets player id
00301     {
00302         ID_array[j] = char_sel(1);
00303     }
00304     for (unsigned j = size; j < 12; j ++)
00305     {
00306         ID_array[j] = ' ';      //fill unused characters with spaces
00307     }
00308     
00309     lcd.cls();
00310     lcd.printf("On team?\n Y or N");
00311     
00312     while(!btn2 && !btn3)   //waits until button input received
00313     {
00314         wait(0.05);     //ensures buttons cannot be pressed extremely fast
00315     }
00316     if(btn3)
00317     {
00318         btn3 = false;
00319         btn2 = false;   //you're on a team if you press both buttons
00320         ID_array[12] = char_sel(0);   //sets team letter
00321     }
00322     else if (btn2)
00323     {
00324         btn2 = false;
00325         ID_array[12] = '0';    //sets team to 'zero', indicating no team
00326     }
00327     else
00328     {
00329         error_custom(); //throws an error if you somehow get here)
00330     }
00331     
00332     lcd.cls();
00333     lcd.printf("Your ID\n is:");
00334     wait(1);
00335     lcd.cls();
00336     for(unsigned j = 0; j <= 12; j++)  //prints out player id and team
00337     {
00338         lcd.printf("%c",ID_array[j]);
00339         if (j == 7)
00340         {
00341             lcd.printf("\n"); //puts in a new line after eight characters
00342         }
00343     }
00344     crc16_attach(ID_array,13);   //attaches crc bits to last two bits of 15 character array
00345     wait(3);
00346     return;
00347 }
00348 
00349 /*ID Checking Routine
00350    Checks received IDs to see if it is a valid shot
00351    player_array is the 15 character id array of the player that constains a 16-bit crc in the last two bytes
00352    Returns a boolean true if shot is valid, otherwise false
00353 */
00354 bool check_id(char* player_array)
00355 {
00356     if (id_rx[12] == player_array[12] && id_rx[12] >= 'A')
00357     {
00358         return false;   //if ids are on the same team or are the same person, they cannot be hit by each other/themselves
00359     }
00360     else if(id_rx[13] == player_array[13] && id_rx[14] == player_array[14])
00361     {
00362         return false;
00363     }
00364     return (crc16_match(id_rx, 13));  //compares the crc tag in received id to a calculated one. Returns false if different.
00365 }
00366 
00367 /*Respawn Display
00368     Displays appropriate information for who player was hit by while they are "respawning"
00369     Returns nothing
00370 */
00371 void respawn()
00372 {
00373     lcd.cls();
00374     lcd.printf("Hit by:");
00375     wait(1);
00376     lcd.cls();
00377     for(unsigned j = 0; j <= 12; j++)  //prints out id and team of person who tagged player
00378     {
00379         lcd.printf("%c",id_rx[j]);
00380         if (j == 7)
00381         {
00382             lcd.printf("\n"); //puts in a new line after eight characters
00383         }
00384     }
00385     wait(4);
00386 }
00387 /*Firing function
00388   Sends ID information through IR
00389   Parameters: player_id
00390   player_id = 15 character array that contains player id and crc hash
00391   returns nothing 
00392 */
00393 void fire(char* player_id)
00394 {
00395     for (unsigned i = 0; i < 15; i ++)
00396     {
00397         IR.putc(player_id[i]);
00398     }
00399 }   
00400 
00401 /*Statistics Display Function
00402     Displays game statistics without interrupting gameplay significantly
00403     Requires LCD to be on.
00404     Function will end if fire button is pressed or 5 seconds have passed
00405     without the statistics button (btn2) being pressed again.
00406     Maximum amount of time possible in this function is 15 seconds,
00407     5 seconds for each of the 3 statistics displays
00408     Parameters: tag_count = number of times player has been tagged
00409                 shots = number of times player has shot
00410                 mode = level of statistics looking at: tag count(0), shots(1), or time since reset(2) 
00411     Returns nothing
00412 */
00413 void statistics(const unsigned &tag_count, const unsigned &shots, short mode)
00414 {
00415     done = false;   //Used to timeout on statistics display
00416     lcd.cls();
00417     if(mode == 0)
00418     {
00419         lcd.printf("Tagged:\n%u",tag_count);
00420     }
00421     else if(mode == 1)
00422     {
00423         lcd.printf("# Shots:\n%u",shots);   //shot count
00424     }
00425     else if(mode == 2)
00426     {
00427         unsigned secs = 0;  //used to readout numer of seconds on timer
00428         secs = timer.read();
00429         lcd.printf("Mins:%u\nSecs:%u", mins,secs);
00430     }
00431     else 
00432     {
00433     led1 = 1;
00434     led2 = 1;
00435     return; //if no valid mode, the function will end. Used to end recursion
00436     }
00437     stats.attach(&finish_disp, 5);
00438     while(!done)
00439     {
00440         if(btn3 == true || tagged)
00441         {
00442             stats.detach(); //exit if something interrupts stats
00443             lcd.cls();
00444             return;
00445         }
00446         else if(btn2 == true)
00447         {
00448             btn2 = false;
00449             stats.detach(); //keeps statistics display from timing out for another 5 seconds
00450             mode ++;
00451             statistics(tag_count,shots,mode);   //recursive function call of statistics
00452             return;     //jumps out of function whenever other forms return
00453         }
00454         wait(0.05);     //function will get stuck without short wait
00455     }
00456     return;
00457 }
00458 
00459 int main() 
00460 {
00461     Square_Gen.period_us(26);       //produces a 38kHz square wave to ensure proper IR communication...
00462     Square_Gen.pulsewidth_us(13);   //...this signal is sent to one of the IR diode terminals through a transistor
00463     IR.baud(1200);          //sets baud rate of serial output and input
00464     IR.attach(&callback);   //attaches interrupt for serial buffer  
00465     LB = 1; //turn on lcd backlight
00466     LP = 1; //turn on lcd power
00467     sw2.mode(PullUp);   //set buttons to a default high voltage for logic low
00468     sw3.mode(PullUp);   
00469     sw2.setSampleFrequency(); //set buttons to default sample frequency for debouncing
00470     sw3.setSampleFrequency();
00471     sw2.setAssertValue(0);  //set buttons to be logic high when voltage is low.
00472     sw3.setAssertValue(0);
00473     sw2.attach_asserted( &btn2Pressed ); //attach button interrupts
00474     sw3.attach_asserted( &btn3Pressed );
00475     clock_tick.attach(&minutes, 60);  //ticker interrupts every 60 seconds to add 1 minute and reset timer
00476     timer.reset();  //ensure timer starts at 0
00477     timer.start();  //start reset timer
00478     
00479     led1 = 1;
00480     char id[15]; //id array, capable of storing user ids up to 12 bytes with 1 byte for team and 2 bytes for CRC.
00481     unsigned tag_count = 0;   //number of times player has been tagged in a game
00482     unsigned shots = 0;       //number of shots fired in a game.
00483     lcd.cls();      //ensure lcd starts with clear screen
00484     lcd.printf("Choose \nYour ID.");
00485     while(btn2 == false && btn3 == false)
00486     {
00487         wait(0.2);      //wait to start setup until either button is pressed
00488     }
00489     btn2 = false;       //reset button values
00490     btn3 = false;
00491     setup(id);      //setup player id and team, passing function the id array
00492     taggable = true;
00493     LB = 0; //turn off lcd screen, backlight, and leds to save power during game
00494     LP = 0;
00495     led1 = 0;
00496     led2 = 0;
00497     while(1) 
00498     {
00499         if(tagged)
00500         {
00501             flash.attach(&flasher,0.5);
00502             tagged = false;
00503             //LB = 1; //turn on backlight and leds for flash
00504             led1 = 1;
00505             led2 = 1;
00506             if (check_id(id))
00507             {
00508                 taggable = false;    //don't allow new tags if respawning
00509                 flash.detach(); //if tag is valid, no need to flash
00510                 tag_count ++;
00511                 LP = 1; //turn LCD power and backlight on 
00512                 LB = 1;
00513                 respawn();  //prints appropriate respawn info
00514                 taggable = true;
00515                 led2 = 0;
00516             }
00517             LB = 0; //turn LCD backlight and power back off
00518             LP = 0;
00519             led1 = 0; 
00520         }
00521         if(btn3)    //fires IR led if btn3 is pressed
00522         {
00523             btn3 = false;
00524             flash.detach(); //lights will not flash if you fire really fast
00525             shots ++;   //increment number of times ir has been shot by player
00526             LB = 1;
00527             led1 = 1;
00528             led2 = 1;
00529             flash.attach(flasher,0.5); //flashes backlight and leds when firing
00530             fire(id);   //fires player id using IR diode
00531         }
00532         if (btn2)   //displays statistics if btn2 is pressed
00533         {
00534             flash.detach();
00535             btn2 = false;
00536             LP = 1; //turn on display
00537             LB = 1;
00538             statistics(tag_count,shots,0);
00539             LP = 0;
00540             LB = 0;
00541         }
00542         wait(0.01);
00543     }
00544 }