Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Start of code (copy and paste into arduino sketch)
- //
- // Duino Tag release V1.01
- // Laser Tag for the arduino based on the Miles Tag Protocol.
- // By J44industries: www.J44industries.blogspot.com
- // For information on building your own Duino Tagger go to: http://www.instructables.com/member/j44/
- //
- // Much credit deserves to go to Duane O'Brien if it had not been for the excellent Duino Tag tutorials he wrote I would have never been able to write this code.
- // Duane's tutorials are highly recommended reading in order to gain a better understanding of the arduino and IR communication. See his site http://aterribleidea.com/duino-tag-resources/
- //
- // This code sets out the basics for arduino based laser tag system and tries to stick to the miles tag protocol where possible.
- // Miles Tag details: http://www.lasertagparts.com/mtdesign.htm
- // There is much scope for expanding the capabilities of this system, and hopefully the game will continue to evolve for some time to come.
- // Licence: Attribution Share Alike: Give credit where credit is due, but you can do what you like with the code.
- // If you have code improvements or additions please go to http://duinotag.blogspot.com
- //
- // Digital IO's
- int triggerPin = 3; // Push button for primary fire. Low = pressed
- int trigger2Pin = 13; // Push button for secondary fire. Low = pressed
- int speakerPin = 4; // Direct output to piezo sounder/speaker
- int audioPin = 9; // Audio Trigger. Can be used to set off sounds recorded in the kind of electronics you can get in greetings card that play a custom message.
- int lifePin = 6; // An analogue output (PWM) level corresponds to remaining life. Use PWM pin: 3,5,6,9,10 or 11. Can be used to drive LED bar graphs. eg LM3914N
- int ammoPin = 5; // An analogue output (PWM) level corresponds to remaining ammunition. Use PWM pin: 3,5,6,9,10 or 11.
- int hitPin = 7; // LED output pin used to indicate when the player has been hit.
- int IRtransmitPin = 2; // Primary fire mode IR transmitter pin: Use pins 2,4,7,8,12 or 13. DO NOT USE PWM pins!! More info: http://j44industries.blogspot.com/2009/09/arduino-frequency-generation.html#more
- int IRtransmit2Pin = 8; // Secondary fire mode IR transmitter pin: Use pins 2,4,7,8,12 or 13. DO NOT USE PWM pins!!
- int IRreceivePin = 12; // The pin that incoming IR signals are read from
- int IRreceive2Pin = 11; // Allows for checking external sensors are attached as well as distinguishing between sensor locations (eg spotting head shots)
- // Minimum gun requirements: trigger, receiver, IR led, hit LED.
- // Player and Game details
- int myTeamID = 1; // 1-7 (0 = system message)
- int myPlayerID = 5; // Player ID
- int myGameID = 0; // Interprited by configureGane subroutine; allows for quick change of game types.
- int myWeaponID = 0; // Deffined by gameType and configureGame subroutine.
- int myWeaponHP = 0; // Deffined by gameType and configureGame subroutine.
- int maxAmmo = 0; // Deffined by gameType and configureGame subroutine.
- int maxLife = 0; // Deffined by gameType and configureGame subroutine.
- int automatic = 0; // Deffined by gameType and configureGame subroutine. Automatic fire 0 = Semi Auto, 1 = Fully Auto.
- int automatic2 = 0; // Deffined by gameType and configureGame subroutine. Secondary fire auto?
- //Incoming signal Details
- int received[18]; // Received data: received[0] = which sensor, received[1] - [17] byte1 byte2 parity (Miles Tag structure)
- int check = 0; // Variable used in parity checking
- // Stats
- int ammo = 0; // Current ammunition
- int life = 0; // Current life
- // Code Variables
- int timeOut = 0; // Deffined in frequencyCalculations (IRpulse + 50)
- int FIRE = 0; // 0 = don't fire, 1 = Primary Fire, 2 = Secondary Fire
- int TR = 0; // Trigger Reading
- int LTR = 0; // Last Trigger Reading
- int T2R = 0; // Trigger 2 Reading (For secondary fire)
- int LT2R = 0; // Last Trigger 2 Reading (For secondary fire)
- // Signal Properties
- int IRpulse = 600; // Basic pulse duration of 600uS MilesTag standard 4*IRpulse for header bit, 2*IRpulse for 1, 1*IRpulse for 0.
- int IRfrequency = 38; // Frequency in kHz Standard values are: 38kHz, 40kHz. Choose dependant on your receiver characteristics
- int IRt = 0; // LED on time to give correct transmission frequency, calculated in setup.
- int IRpulses = 0; // Number of oscillations needed to make a full IRpulse, calculated in setup.
- int header = 4; // Header lenght in pulses. 4 = Miles tag standard
- int maxSPS = 10; // Maximum Shots Per Seconds. Not yet used.
- int TBS = 0; // Time between shots. Not yet used.
- // Transmission data
- int byte1[8]; // String for storing byte1 of the data which gets transmitted when the player fires.
- int byte2[8]; // String for storing byte1 of the data which gets transmitted when the player fires.
- int myParity = 0; // String for storing parity of the data which gets transmitted when the player fires.
- // Received data
- int memory = 10; // Number of signals to be recorded: Allows for the game data to be reviewed after the game, no provision for transmitting / accessing it yet though.
- int hitNo = 0; // Hit number
- // Byte1
- int player[10]; // Array must be as large as memory
- int team[10]; // Array must be as large as memory
- // Byte2
- int weapon[10]; // Array must be as large as memory
- int hp[10]; // Array must be as large as memory
- int parity[10]; // Array must be as large as memory
- void setup() {
- // Serial coms set up to help with debugging.
- Serial.begin(9600);
- Serial.println("Startup...");
- // Pin declarations
- pinMode(triggerPin, INPUT);
- pinMode(trigger2Pin, INPUT);
- pinMode(speakerPin, OUTPUT);
- pinMode(audioPin, OUTPUT);
- pinMode(lifePin, OUTPUT);
- pinMode(ammoPin, OUTPUT);
- pinMode(hitPin, OUTPUT);
- pinMode(IRtransmitPin, OUTPUT);
- pinMode(IRtransmit2Pin, OUTPUT);
- pinMode(IRreceivePin, INPUT);
- pinMode(IRreceive2Pin, INPUT);
- frequencyCalculations(); // Calculates pulse lengths etc for desired frequency
- configureGame(); // Look up and configure game details
- tagCode(); // Based on game details etc works out the data that will be transmitted when a shot is fired
- digitalWrite(triggerPin, HIGH); // Not really needed if your circuit has the correct pull up resistors already but doesn't harm
- digitalWrite(trigger2Pin, HIGH); // Not really needed if your circuit has the correct pull up resistors already but doesn't harm
- for (int i = 1;i < 254;i++) { // Loop plays start up noise
- analogWrite(ammoPin, i);
- playTone((3000-9*i), 2);
- }
- // Next 4 lines initialise the display LEDs
- analogWrite(ammoPin, ((int) ammo));
- analogWrite(lifePin, ((int) life));
- lifeDisplay();
- ammoDisplay();
- Serial.println("Ready....");
- }
- // Main loop most of the code is in the sub routines
- void loop(){
- receiveIR();
- if(FIRE != 0){
- shoot();
- ammoDisplay();
- }
- triggers();
- }
- // SUB ROUTINES
- void ammoDisplay() { // Updates Ammo LED output
- float ammoF;
- ammoF = (260/maxAmmo) * ammo;
- if(ammoF <= 0){ammoF = 0;}
- if(ammoF > 255){ammoF = 255;}
- analogWrite(ammoPin, ((int) ammoF));
- }
- void lifeDisplay() { // Updates Ammo LED output
- float lifeF;
- lifeF = (260/maxLife) * life;
- if(lifeF <= 0){lifeF = 0;}
- if(lifeF > 255){lifeF = 255;}
- analogWrite(lifePin, ((int) lifeF));
- }
- void receiveIR() { // Void checks for an incoming signal and decodes it if it sees one.
- int error = 0;
- if(digitalRead(IRreceivePin) == LOW){ // If the receive pin is low a signal is being received.
- digitalWrite(hitPin,HIGH);
- if(digitalRead(IRreceive2Pin) == LOW){ // Is the incoming signal being received by the head sensors?
- received[0] = 1;
- }
- else{
- received[0] = 0;
- }
- while(digitalRead(IRreceivePin) == LOW){
- }
- for(int i = 1; i <= 17; i++) { // Repeats several times to make sure the whole signal has been received
- received[i] = pulseIn(IRreceivePin, LOW, timeOut); // pulseIn command waits for a pulse and then records its duration in microseconds.
- }
- Serial.print("sensor: "); // Prints if it was a head shot or not.
- Serial.print(received[0]);
- Serial.print("...");
- for(int i = 1; i <= 17; i++) { // Looks at each one of the received pulses
- int receivedTemp[18];
- receivedTemp[i] = 2;
- if(received[i] > (IRpulse - 200) && received[i] < (IRpulse + 200)) {receivedTemp[i] = 0;} // Works out from the pulse length if it was a data 1 or 0 that was received writes result to receivedTemp string
- if(received[i] > (IRpulse + IRpulse - 200) && received[i] < (IRpulse + IRpulse + 200)) {receivedTemp[i] = 1;} // Works out from the pulse length if it was a data 1 or 0 that was received
- received[i] = 3; // Wipes raw received data
- received[i] = receivedTemp[i]; // Inputs interpreted data
- Serial.print(" ");
- Serial.print(received[i]); // Print interpreted data results
- }
- Serial.println(""); // New line to tidy up printed results
- // Parity Check. Was the data received a valid signal?
- check = 0;
- for(int i = 1; i <= 16; i++) {
- if(received[i] == 1){check = check + 1;}
- if(received[i] == 2){error = 1;}
- }
- // Serial.println(check);
- check = check >> 0 & B1;
- // Serial.println(check);
- if(check != received[17]){error = 1;}
- if(error == 0){Serial.println("Valid Signal");}
- else{Serial.println("ERROR");}
- if(error == 0){interpritReceived();}
- digitalWrite(hitPin,LOW);
- }
- }
- void interpritReceived(){ // After a message has been received by the ReceiveIR subroutine this subroutine decidedes how it should react to the data
- if(hitNo == memory){hitNo = 0;} // hitNo sorts out where the data should be stored if statement means old data gets overwritten if too much is received
- team[hitNo] = 0;
- player[hitNo] = 0;
- weapon[hitNo] = 0;
- hp[hitNo] = 0;
- // Next few lines Effectivly converts the binary data into decimal
- // Im sure there must be a much more efficient way of doing this
- if(received[1] == 1){team[hitNo] = team[hitNo] + 4;}
- if(received[2] == 1){team[hitNo] = team[hitNo] + 2;}
- if(received[3] == 1){team[hitNo] = team[hitNo] + 1;}
- if(received[4] == 1){player[hitNo] = player[hitNo] + 16;}
- if(received[5] == 1){player[hitNo] = player[hitNo] + 8;}
- if(received[6] == 1){player[hitNo] = player[hitNo] + 4;}
- if(received[7] == 1){player[hitNo] = player[hitNo] + 2;}
- if(received[8] == 1){player[hitNo] = player[hitNo] + 1;}
- if(received[9] == 1){weapon[hitNo] = weapon[hitNo] + 4;}
- if(received[10] == 1){weapon[hitNo] = weapon[hitNo] + 2;}
- if(received[11] == 1){weapon[hitNo] = weapon[hitNo] + 1;}
- if(received[12] == 1){hp[hitNo] = hp[hitNo] + 16;}
- if(received[13] == 1){hp[hitNo] = hp[hitNo] + 8;}
- if(received[14] == 1){hp[hitNo] = hp[hitNo] + 4;}
- if(received[15] == 1){hp[hitNo] = hp[hitNo] + 2;}
- if(received[16] == 1){hp[hitNo] = hp[hitNo] + 1;}
- parity[hitNo] = received[17];
- Serial.print("Hit No: ");
- Serial.print(hitNo);
- Serial.print(" Player: ");
- Serial.print(player[hitNo]);
- Serial.print(" Team: ");
- Serial.print(team[hitNo]);
- Serial.print(" Weapon: ");
- Serial.print(weapon[hitNo]);
- Serial.print(" HP: ");
- Serial.print(hp[hitNo]);
- Serial.print(" Parity: ");
- Serial.println(parity[hitNo]);
- //This is probably where more code should be added to expand the game capabilities at the moment the code just checks that the received data was not a system message and deducts a life if it wasn't.
- if (player[hitNo] != 0){hit();}
- hitNo++ ;
- }
- void shoot() {
- if(FIRE == 1){ // Has the trigger been pressed?
- Serial.println("FIRE 1");
- sendPulse(IRtransmitPin, 4); // Transmit Header pulse, send pulse subroutine deals with the details
- delayMicroseconds(IRpulse);
- for(int i = 0; i < 8; i++) { // Transmit Byte1
- if(byte1[i] == 1){
- sendPulse(IRtransmitPin, 1);
- //Serial.print("1 ");
- }
- //else{Serial.print("0 ");}
- sendPulse(IRtransmitPin, 1);
- delayMicroseconds(IRpulse);
- }
- for(int i = 0; i < 8; i++) { // Transmit Byte2
- if(byte2[i] == 1){
- sendPulse(IRtransmitPin, 1);
- // Serial.print("1 ");
- }
- //else{Serial.print("0 ");}
- sendPulse(IRtransmitPin, 1);
- delayMicroseconds(IRpulse);
- }
- if(myParity == 1){ // Parity
- sendPulse(IRtransmitPin, 1);
- }
- sendPulse(IRtransmitPin, 1);
- delayMicroseconds(IRpulse);
- Serial.println("");
- Serial.println("DONE 1");
- }
- if(FIRE == 2){ // Where a secondary fire mode would be added
- Serial.println("FIRE 2");
- sendPulse(IRtransmitPin, 4); // Header
- Serial.println("DONE 2");
- }
- FIRE = 0;
- ammo = ammo - 1;
- }
- void sendPulse(int pin, int length){ // importing variables like this allows for secondary fire modes etc.
- // This void genertates the carrier frequency for the information to be transmitted
- int i = 0;
- int o = 0;
- while( i < length ){
- i++;
- while( o < IRpulses ){
- o++;
- digitalWrite(pin, HIGH);
- delayMicroseconds(IRt);
- digitalWrite(pin, LOW);
- delayMicroseconds(IRt);
- }
- }
- }
- void triggers() { // Checks to see if the triggers have been presses
- LTR = TR; // Records previous state. Primary fire
- LT2R = T2R; // Records previous state. Secondary fire
- TR = digitalRead(triggerPin); // Looks up current trigger button state
- T2R = digitalRead(trigger2Pin); // Looks up current trigger button state
- // Code looks for changes in trigger state to give it a semi automatic shooting behaviour
- if(TR != LTR && TR == LOW){
- FIRE = 1;
- }
- if(T2R != LT2R && T2R == LOW){
- FIRE = 2;
- }
- if(TR == LOW && automatic == 1){
- FIRE = 1;
- }
- if(T2R == LOW && automatic2 == 1){
- FIRE = 2;
- }
- if(FIRE == 1 || FIRE == 2){
- if(ammo < 1){FIRE = 0; noAmmo();}
- if(life < 1){FIRE = 0; dead();}
- // Fire rate code to be added here
- }
- }
- void configureGame() { // Where the game characteristics are stored, allows several game types to be recorded and you only have to change one variable (myGameID) to pick the game.
- if(myGameID == 0){
- myWeaponID = 1;
- maxAmmo = 30;
- ammo = 30;
- maxLife = 3;
- life = 3;
- myWeaponHP = 1;
- }
- if(myGameID == 1){
- myWeaponID = 1;
- maxAmmo = 100;
- ammo = 100;
- maxLife = 10;
- life = 10;
- myWeaponHP = 2;
- }
- }
- void frequencyCalculations() { // Works out all the timings needed to give the correct carrier frequency for the IR signal
- IRt = (int) (500/IRfrequency);
- IRpulses = (int) (IRpulse / (2*IRt));
- IRt = IRt - 4;
- // Why -4 I hear you cry. It allows for the time taken for commands to be executed.
- // More info: http://j44industries.blogspot.com/2009/09/arduino-frequency-generation.html#more
- Serial.print("Oscilation time period /2: ");
- Serial.println(IRt);
- Serial.print("Pulses: ");
- Serial.println(IRpulses);
- timeOut = IRpulse + 50; // Adding 50 to the expected pulse time gives a little margin for error on the pulse read time out value
- }
- void tagCode() { // Works out what the players tagger code (the code that is transmitted when they shoot) is
- byte1[0] = myTeamID >> 2 & B1;
- byte1[1] = myTeamID >> 1 & B1;
- byte1[2] = myTeamID >> 0 & B1;
- byte1[3] = myPlayerID >> 4 & B1;
- byte1[4] = myPlayerID >> 3 & B1;
- byte1[5] = myPlayerID >> 2 & B1;
- byte1[6] = myPlayerID >> 1 & B1;
- byte1[7] = myPlayerID >> 0 & B1;
- byte2[0] = myWeaponID >> 2 & B1;
- byte2[1] = myWeaponID >> 1 & B1;
- byte2[2] = myWeaponID >> 0 & B1;
- byte2[3] = myWeaponHP >> 4 & B1;
- byte2[4] = myWeaponHP >> 3 & B1;
- byte2[5] = myWeaponHP >> 2 & B1;
- byte2[6] = myWeaponHP >> 1 & B1;
- byte2[7] = myWeaponHP >> 0 & B1;
- myParity = 0;
- for (int i=0; i<8; i++) {
- if(byte1[i] == 1){myParity = myParity + 1;}
- if(byte2[i] == 1){myParity = myParity + 1;}
- myParity = myParity >> 0 & B1;
- }
- // Next few lines print the full tagger code.
- Serial.print("Byte1: ");
- Serial.print(byte1[0]);
- Serial.print(byte1[1]);
- Serial.print(byte1[2]);
- Serial.print(byte1[3]);
- Serial.print(byte1[4]);
- Serial.print(byte1[5]);
- Serial.print(byte1[6]);
- Serial.print(byte1[7]);
- Serial.println();
- Serial.print("Byte2: ");
- Serial.print(byte2[0]);
- Serial.print(byte2[1]);
- Serial.print(byte2[2]);
- Serial.print(byte2[3]);
- Serial.print(byte2[4]);
- Serial.print(byte2[5]);
- Serial.print(byte2[6]);
- Serial.print(byte2[7]);
- Serial.println();
- Serial.print("Parity: ");
- Serial.print(myParity);
- Serial.println();
- }
- void playTone(int tone, int duration) { // A sub routine for playing tones like the standard arduino melody example
- for (long i = 0; i < duration * 1000L; i += tone * 2) {
- digitalWrite(speakerPin, HIGH);
- delayMicroseconds(tone);
- digitalWrite(speakerPin, LOW);
- delayMicroseconds(tone);
- }
- }
- void dead() { // void determines what the tagger does when it is out of lives
- // Makes a few noises and flashes some lights
- for (int i = 1;i < 254;i++) {
- analogWrite(ammoPin, i);
- playTone((1000+9*i), 2);
- }
- analogWrite(ammoPin, ((int) ammo));
- analogWrite(lifePin, ((int) life));
- Serial.println("DEAD");
- for (int i=0; i<10; i++) {
- analogWrite(ammoPin, 255);
- digitalWrite(hitPin,HIGH);
- delay (500);
- analogWrite(ammoPin, 0);
- digitalWrite(hitPin,LOW);
- delay (500);
- }
- }
- void noAmmo() { // Make some noise and flash some lights when out of ammo
- digitalWrite(hitPin,HIGH);
- playTone(500, 100);
- playTone(1000, 100);
- digitalWrite(hitPin,LOW);
- }
- void hit() { // Make some noise and flash some lights when you get shot
- digitalWrite(hitPin,HIGH);
- life = life - hp[hitNo];
- Serial.print("Life: ");
- Serial.println(life);
- playTone(500, 500);
- if(life <= 0){dead();}
- digitalWrite(hitPin,LOW);
- lifeDisplay();
- }
Add Comment
Please, Sign In to add comment