Duino tag is a laser tag system based around the arduino.
Finally a laser tag system that can be tweaked modded and hacked until you have the perfect laser tag system for office ordnance, woodland wars and suburban skirmishes.
Laser tag is combat game like paintball or airsoft without the pain, it uses infrared light (IR) to simulate the tagging / shooting of other players or targets.
I have been working on this project for a while, but don't see it as over, I just though it was time to get more people involved. Hopefully this instructable will be near enough finished in time for me to enter it in the arduino competition, although I expect the instructable will need editing and tweaking for some time to come.
This instructable aims to provide you with the information you will need to go out and build your own duino tagger.
This instructable focuses on how to build a duino tagger by modifying a light gun but with a bit of extra work you could build you own gun from scratch.
This instructable does not look in too much detail at the software / code side of the project, although a working code based on the miles tag protocol is provided.
For those wishing to learn about duino tagger programming I suggest you start at the excellent tutorials found at A Terrible Idea.
Thoes experience arduino users will probably find the overview page (Step 1) and code pages (Step 8) the most useful, newer arduino users may need to take a closer look at the instructable and the links provided.
I hope many of you will find this instructable useful and will go on to build your own duino taggers. There is much scope for improving and upgrading this system outlined here. If you do go on to improve on this duinotagger please share your work and hopefully in time the system will evolve into a much richer gaming experience.
Youtube videos of my duino taggers:
This video shows me using the second duino tagger I made to shoot at a talcapult target I have been working on. I hope to make an instructable about the talcapult soon.
Remove these ads by
Signing UpStep 1: Overview
Key parts of a duino tagger:
IR Transmitter system
What - Transistor amplifier, high power IR LED and a lens to give a well focused IR beam.
Why - To give the tagger means of tagging / shooting other players as well as communicating game information. The transmitter amplifies the transmission signal from the arduino and transmits it using an IR LED, lenses are used to make the signal very directional in order to make the guns behave like guns and make long range transmissions possible.
Sound
What - Peizo Sounder (& mini sound recorder / playback electronics from greetings card)
Why - It’s nice to have some audio feedback from the system. Sound effects to let you know when the tagger has been shot or run out of ammunition or lives.
Receivers
What - Standard IR receiver module and power regulation capacitor
Why - So the gun knows when it has been shot. This design in this instructable looks at using 3 sensors 1 on the gun as well as front and rear head sensors.
Visual Effects and lights
What - LED’s and bar graph driver circuits
Why - It is nice to get visual feedback on game information, eg lights to let you know when you have been shot.
Cost
To make this section internationally friendly; at the time of writing: £1 GBP = $ 1.6 USD = 1.1 EUR
Assuming you already own an arduino the basic extra components for this project can be bought for around £10.
Now is a great time to try to build a duino tagger as it is easy to get hold of cheap second hand light guns. Light guns are the guns that you can use with computer games, many of them don’t work with modern LCD screens and so there are a lot of them going cheap on ebay and else where. I bought two light guns each cost about £4 one off ebay one from a charity shop. Light guns are a great starting point for this project as they provide the optics, switches and case required for the build.
You will need:
Arduino
Light Gun
Coloured LED’s (preferably 30mA+)
IR sensors
At least 2x IR LED’s matched to the IR receiver (preferably 100mA+)
Peizo sounder
Power transistor /MOSFET
A few electronics basics: solder, resistors , capacitors.
You may also want
Scrap plastic
LED bar graph driver chips
More LED’s
Record your own message greetings card
Hats / helmets / headbands to mount sensors on













































Visit Our Store »
Go Pro Today »




I wanted to start on this project with an arduino nano and so i bought a light gun. Except this gun is rather complicated and has a recoil blowback feature. It looks extremely different to the one you have, and i dont know where to start =\. Im a total arduino noob and ive never done an arduino project before, but i was wondering whether it was possible to use the same IR firing internals without replacing the LED (because as you can see its the little silver box) as well as powering the recoil feature. From the picture you can see the wiring to the bottom, that is an actual laser pointer.
first problem:
ERROR
sensor: 1 ... 2 2 2 2 0 2 2 0 2 2 0 2 2 2 2 2 2
as Keanan,
I followed the advice:
Try removing timeOut paramter in le seguenti line of the code:
received [i] = pulseIn (IRreceivePin, LOW, timeOut);
and did not work,
I changed the output pin of the IR, I put A2, and began to see the signal,
but sees alternately:
Ready ....
sensor: 0 ... 0 0 2 0 0 1 0 1 0 0 1 0 0 0 0 1 1
ERROR
sensor: 0 ... 0 0 1 0 0 1 0 1 0 0 1 0 0 0 0 1 1
Valid Signal
Hit No: 0 Player: 5 Team: 1 Weapon: 1 HP: 1 Parity: 1
Life: 2
sensor: 0 ... 0 0 2 0 0 2 0 2 0 0 2 0 0 0 0 1 1
ERROR
sensor: 0 ... 0 0 2 0 0 2 0 2 0 0 1 0 0 0 0 1 1
ERROR
sensor: 0 ... 0 0 1 0 0 1 0 1 0 0 1 0 0 0 0 1 1
Valid Signal
Hit No: 1 Player: 5 Team: 1 Weapon: 1 HP: 1 Parity: 1
Life: 1
sensor: 0 ... 0 0 1 0 0 1 0 1 0 0 1 0 0 0 0 1 1
Valid Signal
Hit No: 2 Player: 5 Team: 1 Weapon: 1 HP: 1 Parity: 1
Life: 0
DEAD
What solution do you recommend?
I don't really have the time now to sit down and rewrite it but if you look through some of the comments below I have made a few suggestions as to what the problems might be.
And a big thank YOU for this instruction. Real good.
Gota try and make this myself and mabe add things.. Have done some Arduino codeing in past.
Thanks Again.
Hargard
// 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.
if(digitalRead(IRreceivePin) == LOW){ // If the receive pin is low a signal is being received.
digitalWrite(hitPin,HIGH);
hit();
}
}
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 atone, int duration) { // A sub routine for playing tones like the standard arduino melody example
for (long i = 0; i < duration * 1000L; i += atone * 2) {
digitalWrite(speakerPin, HIGH);
delayMicroseconds(atone);
digitalWrite(speakerPin, LOW);
delayMicroseconds(atone);
}
}
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 - 1;
Serial.print("Life: ");
Serial.println(life);
playTone(500, 500);
if(life <= 0){dead();}
digitalWrite(hitPin,LOW);
lifeDisplay();
}
// 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 = 56; // 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 atone, int duration) { // A sub routine for playing tones like the standard arduino melody example
for (long i = 0; i < duration * 1000L; i += atone * 2) {
digitalWrite(speakerPin, HIGH);
delayMicroseconds(atone);
digitalWrite(speakerPin, LOW);
delayMicroseconds(atone);
}
}
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();
}
One question though, wouldnt a laser work the same way (i know the hazard might be the problem) but isnt that what is used for most laser tag systems?
As a suggestion, make a simple voltage regulator for the arduino and add a bigger battery to be able to use the motor, but I am not sure how to signal the motor to move (Im thinking using a PWM system?).
All laser tag systems that I know of use IR diodes, although some systems have a low power laser as well just to show where you have shot. If you were to just use a laser then you would find the bean would be far too narrow; you would either have to coat the players head to toe in sensors or aim very very accurately at the sensors, I think you would struggle to get a realistic game feel.
I have got force feedback motors working with the arduino, but I wanted to keep these guns small and relativly simple so didn't want to bother with a bigger battery pack so disabled the feature.
Let me know how you get on.
http://www.hobbyking.com/hobbyking/store/__11938__Turnigy_nano_tech_6000mah_2S_25_50C_Lipo_Pack.html
Cute, little, and strong.
Its almost you have a vested interest in advertising SparkFun products :-)
So I will say the following:
I have recently used the MP3 Trigger V2 to add sound to a project and it was great (easy to use and worked very well).
http://www.sparkfun.com/commerce/product_info.php?products_id=9715
Would be a great way of improving the sound effect on a duino tagger.
I have also had very good experiences with several arduino pro minis recently.
http://www.sparkfun.com/commerce/product_info.php?products_id=9218
Great for any compact duino tag projects.
So credit where credit is due I have had good experience with SparkFun products.
Still think it would be cool to have a duino tagger android app ;-P
This arena also has "bases" which you have to shoot 3 times (with 3 second down time between each shot at the base) and then you capture it and score 10 points in stead of 1.
Feedback motors are cool, but as a "laser" feel it's alright if they're kept off, to keep with the futuristic feel of just the laser.
You might have more luck searching for the brand names and models eg:
Namco
G-Con
Logi-3
Joytech
Once again Thank you for your tutorial and help.
"AerospaceSmith" <(^^,)>
Using a code from Ladyada (https://github.com/adafruit/Raw-IR-decoder-for-Arduino/blob/master/rawirdecode.pde) I was able to read the raw IR code sent from the other arduino(which was sending 56kHz at the time). So it seems to be the lasertag code can't read fast enough. I am in urgent need of help or a new code as soon as possible. I just need a simple laser tag code to use between mainly two people playing against each other that I can have each players score keep track of independently. Any and all help will be greatly appreciated.
Yeshi88 advice is good try looking at what length pulses you are receiving.
If the values don't come out quite right a simple fix could be:
Add a line in void FrequencyCalculations after line
IRpulses = (int) (IRpulse / (2*IRt));
to
IRpulses = (int) (IRpulse / (2*IRt));
IRpulses = IRpulses - 5;
Where I have put - 5 try a few values between about + 10 and - 10 and see if that sorts things out.
Failing that in the set up change Serial.begin(9600); to Serial.begin(57600);
Note you will also have to change the baud in the Serial Monitor. I dont think it is particularly likely to help but in the latest IDE they do seem to have changed some serial stuff and if it has got slower perhaps that could be a problem.
Sorry I don't really have the facilities to replicate the fault at the moment but hopefully some of the suggestions might help.
sensor: 1... 2 2 2 2 0 2 2 0 2 2 0 2 2 2 2 2 2
after the sometimes there is all 2s and sometimes zeros randomly in the string. I also set it up to display received[17] specifically and it is always 2. Why is this?
I also have the Arduinos close to each other, could the short distance be affecting the IR signal?
Any advise would be greatly appreciated.
received[i] = pulseIn(IRreceivePin, LOW, timeOut);
If it still does not work , try adding
Serial.println(received[i]); before the following lines.
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
Your output should fall between 400 - 800 for byte 0 and 1000 - 1400 for byte 1
My lasertag used to work and as of today I am getting the above error as well ?
I have tried J44's suggestions and still nothing? Weird !
Any other suggestions ?
Thanks
Tag
Thanks
Tag
The code uses rather basic bit bashing to generate the frequencies and may not be making the pulses the right length with the frequency set to 56k, meaning the receive arduino is rejecting the pulses as not valid.
The best thing to do would be to look at the signal to the transmitter LED on an oscilloscope and make sure the pulses are 600us for 0's and 1200us for 1's, but there are some other simpler fault finding tests you can try.
If you put IRfrequency variable back to 38 and try it you might find it works (even though a 56k reciever is not designed to recieve that frequency it will be slightly sensitive to it and might just give you short ranges.
If that works put the frequency back to 56 then try the following:
In the frequencyCalculation void there is a line IRt = IRt - 4. Try -3 or -5 instead of the -4 and see if that makes it work.
Thanks!
Will