Guest User

Duino Tag (code)

a guest
Jan 10th, 2020
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.31 KB | None | 0 0
  1. // Start of code (copy and paste into arduino sketch)
  2. //
  3. // Duino Tag release V1.01
  4. // Laser Tag for the arduino based on the Miles Tag Protocol.
  5. // By J44industries: www.J44industries.blogspot.com
  6. // For information on building your own Duino Tagger go to: http://www.instructables.com/member/j44/
  7. //
  8. // 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.
  9. // 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/
  10. //
  11. // This code sets out the basics for arduino based laser tag system and tries to stick to the miles tag protocol where possible.
  12. // Miles Tag details: http://www.lasertagparts.com/mtdesign.htm
  13. // There is much scope for expanding the capabilities of this system, and hopefully the game will continue to evolve for some time to come.
  14. // Licence: Attribution Share Alike: Give credit where credit is due, but you can do what you like with the code.
  15. // If you have code improvements or additions please go to http://duinotag.blogspot.com
  16. //
  17.  
  18.  
  19.  
  20. // Digital IO's
  21. int triggerPin = 3; // Push button for primary fire. Low = pressed
  22. int trigger2Pin = 13; // Push button for secondary fire. Low = pressed
  23. int speakerPin = 4; // Direct output to piezo sounder/speaker
  24. 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.
  25. 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
  26. int ammoPin = 5; // An analogue output (PWM) level corresponds to remaining ammunition. Use PWM pin: 3,5,6,9,10 or 11.
  27. int hitPin = 7; // LED output pin used to indicate when the player has been hit.
  28. 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
  29. int IRtransmit2Pin = 8; // Secondary fire mode IR transmitter pin: Use pins 2,4,7,8,12 or 13. DO NOT USE PWM pins!!
  30. int IRreceivePin = 12; // The pin that incoming IR signals are read from
  31. int IRreceive2Pin = 11; // Allows for checking external sensors are attached as well as distinguishing between sensor locations (eg spotting head shots)
  32. // Minimum gun requirements: trigger, receiver, IR led, hit LED.
  33.  
  34. // Player and Game details
  35. int myTeamID = 1; // 1-7 (0 = system message)
  36. int myPlayerID = 5; // Player ID
  37. int myGameID = 0; // Interprited by configureGane subroutine; allows for quick change of game types.
  38. int myWeaponID = 0; // Deffined by gameType and configureGame subroutine.
  39. int myWeaponHP = 0; // Deffined by gameType and configureGame subroutine.
  40. int maxAmmo = 0; // Deffined by gameType and configureGame subroutine.
  41. int maxLife = 0; // Deffined by gameType and configureGame subroutine.
  42. int automatic = 0; // Deffined by gameType and configureGame subroutine. Automatic fire 0 = Semi Auto, 1 = Fully Auto.
  43. int automatic2 = 0; // Deffined by gameType and configureGame subroutine. Secondary fire auto?
  44.  
  45. //Incoming signal Details
  46. int received[18]; // Received data: received[0] = which sensor, received[1] - [17] byte1 byte2 parity (Miles Tag structure)
  47. int check = 0; // Variable used in parity checking
  48.  
  49. // Stats
  50. int ammo = 0; // Current ammunition
  51. int life = 0; // Current life
  52.  
  53. // Code Variables
  54. int timeOut = 0; // Deffined in frequencyCalculations (IRpulse + 50)
  55. int FIRE = 0; // 0 = don't fire, 1 = Primary Fire, 2 = Secondary Fire
  56. int TR = 0; // Trigger Reading
  57. int LTR = 0; // Last Trigger Reading
  58. int T2R = 0; // Trigger 2 Reading (For secondary fire)
  59. int LT2R = 0; // Last Trigger 2 Reading (For secondary fire)
  60.  
  61. // Signal Properties
  62. int IRpulse = 600; // Basic pulse duration of 600uS MilesTag standard 4*IRpulse for header bit, 2*IRpulse for 1, 1*IRpulse for 0.
  63. int IRfrequency = 38; // Frequency in kHz Standard values are: 38kHz, 40kHz. Choose dependant on your receiver characteristics
  64. int IRt = 0; // LED on time to give correct transmission frequency, calculated in setup.
  65. int IRpulses = 0; // Number of oscillations needed to make a full IRpulse, calculated in setup.
  66. int header = 4; // Header lenght in pulses. 4 = Miles tag standard
  67. int maxSPS = 10; // Maximum Shots Per Seconds. Not yet used.
  68. int TBS = 0; // Time between shots. Not yet used.
  69.  
  70. // Transmission data
  71. int byte1[8]; // String for storing byte1 of the data which gets transmitted when the player fires.
  72. int byte2[8]; // String for storing byte1 of the data which gets transmitted when the player fires.
  73. int myParity = 0; // String for storing parity of the data which gets transmitted when the player fires.
  74.  
  75. // Received data
  76. 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.
  77. int hitNo = 0; // Hit number
  78. // Byte1
  79. int player[10]; // Array must be as large as memory
  80. int team[10]; // Array must be as large as memory
  81. // Byte2
  82. int weapon[10]; // Array must be as large as memory
  83. int hp[10]; // Array must be as large as memory
  84. int parity[10]; // Array must be as large as memory
  85.  
  86.  
  87. void setup() {
  88. // Serial coms set up to help with debugging.
  89. Serial.begin(9600);
  90. Serial.println("Startup...");
  91. // Pin declarations
  92. pinMode(triggerPin, INPUT);
  93. pinMode(trigger2Pin, INPUT);
  94. pinMode(speakerPin, OUTPUT);
  95. pinMode(audioPin, OUTPUT);
  96. pinMode(lifePin, OUTPUT);
  97. pinMode(ammoPin, OUTPUT);
  98. pinMode(hitPin, OUTPUT);
  99. pinMode(IRtransmitPin, OUTPUT);
  100. pinMode(IRtransmit2Pin, OUTPUT);
  101. pinMode(IRreceivePin, INPUT);
  102. pinMode(IRreceive2Pin, INPUT);
  103.  
  104. frequencyCalculations(); // Calculates pulse lengths etc for desired frequency
  105. configureGame(); // Look up and configure game details
  106. tagCode(); // Based on game details etc works out the data that will be transmitted when a shot is fired
  107.  
  108.  
  109. digitalWrite(triggerPin, HIGH); // Not really needed if your circuit has the correct pull up resistors already but doesn't harm
  110. digitalWrite(trigger2Pin, HIGH); // Not really needed if your circuit has the correct pull up resistors already but doesn't harm
  111.  
  112. for (int i = 1;i < 254;i++) { // Loop plays start up noise
  113. analogWrite(ammoPin, i);
  114. playTone((3000-9*i), 2);
  115. }
  116.  
  117. // Next 4 lines initialise the display LEDs
  118. analogWrite(ammoPin, ((int) ammo));
  119. analogWrite(lifePin, ((int) life));
  120. lifeDisplay();
  121. ammoDisplay();
  122.  
  123. Serial.println("Ready....");
  124. }
  125.  
  126.  
  127. // Main loop most of the code is in the sub routines
  128. void loop(){
  129. receiveIR();
  130. if(FIRE != 0){
  131. shoot();
  132. ammoDisplay();
  133. }
  134. triggers();
  135. }
  136.  
  137.  
  138. // SUB ROUTINES
  139.  
  140.  
  141. void ammoDisplay() { // Updates Ammo LED output
  142. float ammoF;
  143. ammoF = (260/maxAmmo) * ammo;
  144. if(ammoF <= 0){ammoF = 0;}
  145. if(ammoF > 255){ammoF = 255;}
  146. analogWrite(ammoPin, ((int) ammoF));
  147. }
  148.  
  149.  
  150. void lifeDisplay() { // Updates Ammo LED output
  151. float lifeF;
  152. lifeF = (260/maxLife) * life;
  153. if(lifeF <= 0){lifeF = 0;}
  154. if(lifeF > 255){lifeF = 255;}
  155. analogWrite(lifePin, ((int) lifeF));
  156. }
  157.  
  158.  
  159. void receiveIR() { // Void checks for an incoming signal and decodes it if it sees one.
  160. int error = 0;
  161.  
  162. if(digitalRead(IRreceivePin) == LOW){ // If the receive pin is low a signal is being received.
  163. digitalWrite(hitPin,HIGH);
  164. if(digitalRead(IRreceive2Pin) == LOW){ // Is the incoming signal being received by the head sensors?
  165. received[0] = 1;
  166. }
  167. else{
  168. received[0] = 0;
  169. }
  170.  
  171. while(digitalRead(IRreceivePin) == LOW){
  172. }
  173. for(int i = 1; i <= 17; i++) { // Repeats several times to make sure the whole signal has been received
  174. received[i] = pulseIn(IRreceivePin, LOW, timeOut); // pulseIn command waits for a pulse and then records its duration in microseconds.
  175. }
  176.  
  177. Serial.print("sensor: "); // Prints if it was a head shot or not.
  178. Serial.print(received[0]);
  179. Serial.print("...");
  180.  
  181. for(int i = 1; i <= 17; i++) { // Looks at each one of the received pulses
  182. int receivedTemp[18];
  183. receivedTemp[i] = 2;
  184. 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
  185. 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
  186. received[i] = 3; // Wipes raw received data
  187. received[i] = receivedTemp[i]; // Inputs interpreted data
  188.  
  189. Serial.print(" ");
  190. Serial.print(received[i]); // Print interpreted data results
  191. }
  192. Serial.println(""); // New line to tidy up printed results
  193.  
  194. // Parity Check. Was the data received a valid signal?
  195. check = 0;
  196. for(int i = 1; i <= 16; i++) {
  197. if(received[i] == 1){check = check + 1;}
  198. if(received[i] == 2){error = 1;}
  199. }
  200. // Serial.println(check);
  201. check = check >> 0 & B1;
  202. // Serial.println(check);
  203. if(check != received[17]){error = 1;}
  204. if(error == 0){Serial.println("Valid Signal");}
  205. else{Serial.println("ERROR");}
  206. if(error == 0){interpritReceived();}
  207. digitalWrite(hitPin,LOW);
  208. }
  209. }
  210.  
  211.  
  212. void interpritReceived(){ // After a message has been received by the ReceiveIR subroutine this subroutine decidedes how it should react to the data
  213. 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
  214. team[hitNo] = 0;
  215. player[hitNo] = 0;
  216. weapon[hitNo] = 0;
  217. hp[hitNo] = 0;
  218. // Next few lines Effectivly converts the binary data into decimal
  219. // Im sure there must be a much more efficient way of doing this
  220. if(received[1] == 1){team[hitNo] = team[hitNo] + 4;}
  221. if(received[2] == 1){team[hitNo] = team[hitNo] + 2;}
  222. if(received[3] == 1){team[hitNo] = team[hitNo] + 1;}
  223.  
  224. if(received[4] == 1){player[hitNo] = player[hitNo] + 16;}
  225. if(received[5] == 1){player[hitNo] = player[hitNo] + 8;}
  226. if(received[6] == 1){player[hitNo] = player[hitNo] + 4;}
  227. if(received[7] == 1){player[hitNo] = player[hitNo] + 2;}
  228. if(received[8] == 1){player[hitNo] = player[hitNo] + 1;}
  229.  
  230. if(received[9] == 1){weapon[hitNo] = weapon[hitNo] + 4;}
  231. if(received[10] == 1){weapon[hitNo] = weapon[hitNo] + 2;}
  232. if(received[11] == 1){weapon[hitNo] = weapon[hitNo] + 1;}
  233.  
  234. if(received[12] == 1){hp[hitNo] = hp[hitNo] + 16;}
  235. if(received[13] == 1){hp[hitNo] = hp[hitNo] + 8;}
  236. if(received[14] == 1){hp[hitNo] = hp[hitNo] + 4;}
  237. if(received[15] == 1){hp[hitNo] = hp[hitNo] + 2;}
  238. if(received[16] == 1){hp[hitNo] = hp[hitNo] + 1;}
  239.  
  240. parity[hitNo] = received[17];
  241.  
  242. Serial.print("Hit No: ");
  243. Serial.print(hitNo);
  244. Serial.print(" Player: ");
  245. Serial.print(player[hitNo]);
  246. Serial.print(" Team: ");
  247. Serial.print(team[hitNo]);
  248. Serial.print(" Weapon: ");
  249. Serial.print(weapon[hitNo]);
  250. Serial.print(" HP: ");
  251. Serial.print(hp[hitNo]);
  252. Serial.print(" Parity: ");
  253. Serial.println(parity[hitNo]);
  254.  
  255.  
  256. //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.
  257. if (player[hitNo] != 0){hit();}
  258. hitNo++ ;
  259. }
  260.  
  261.  
  262. void shoot() {
  263. if(FIRE == 1){ // Has the trigger been pressed?
  264. Serial.println("FIRE 1");
  265. sendPulse(IRtransmitPin, 4); // Transmit Header pulse, send pulse subroutine deals with the details
  266. delayMicroseconds(IRpulse);
  267.  
  268. for(int i = 0; i < 8; i++) { // Transmit Byte1
  269. if(byte1[i] == 1){
  270. sendPulse(IRtransmitPin, 1);
  271. //Serial.print("1 ");
  272. }
  273. //else{Serial.print("0 ");}
  274. sendPulse(IRtransmitPin, 1);
  275. delayMicroseconds(IRpulse);
  276. }
  277.  
  278. for(int i = 0; i < 8; i++) { // Transmit Byte2
  279. if(byte2[i] == 1){
  280. sendPulse(IRtransmitPin, 1);
  281. // Serial.print("1 ");
  282. }
  283. //else{Serial.print("0 ");}
  284. sendPulse(IRtransmitPin, 1);
  285. delayMicroseconds(IRpulse);
  286. }
  287.  
  288. if(myParity == 1){ // Parity
  289. sendPulse(IRtransmitPin, 1);
  290. }
  291. sendPulse(IRtransmitPin, 1);
  292. delayMicroseconds(IRpulse);
  293. Serial.println("");
  294. Serial.println("DONE 1");
  295. }
  296.  
  297.  
  298. if(FIRE == 2){ // Where a secondary fire mode would be added
  299. Serial.println("FIRE 2");
  300. sendPulse(IRtransmitPin, 4); // Header
  301. Serial.println("DONE 2");
  302. }
  303. FIRE = 0;
  304. ammo = ammo - 1;
  305. }
  306.  
  307.  
  308. void sendPulse(int pin, int length){ // importing variables like this allows for secondary fire modes etc.
  309. // This void genertates the carrier frequency for the information to be transmitted
  310. int i = 0;
  311. int o = 0;
  312. while( i < length ){
  313. i++;
  314. while( o < IRpulses ){
  315. o++;
  316. digitalWrite(pin, HIGH);
  317. delayMicroseconds(IRt);
  318. digitalWrite(pin, LOW);
  319. delayMicroseconds(IRt);
  320. }
  321. }
  322. }
  323.  
  324.  
  325. void triggers() { // Checks to see if the triggers have been presses
  326. LTR = TR; // Records previous state. Primary fire
  327. LT2R = T2R; // Records previous state. Secondary fire
  328. TR = digitalRead(triggerPin); // Looks up current trigger button state
  329. T2R = digitalRead(trigger2Pin); // Looks up current trigger button state
  330. // Code looks for changes in trigger state to give it a semi automatic shooting behaviour
  331. if(TR != LTR && TR == LOW){
  332. FIRE = 1;
  333. }
  334. if(T2R != LT2R && T2R == LOW){
  335. FIRE = 2;
  336. }
  337. if(TR == LOW && automatic == 1){
  338. FIRE = 1;
  339. }
  340. if(T2R == LOW && automatic2 == 1){
  341. FIRE = 2;
  342. }
  343. if(FIRE == 1 || FIRE == 2){
  344. if(ammo < 1){FIRE = 0; noAmmo();}
  345. if(life < 1){FIRE = 0; dead();}
  346. // Fire rate code to be added here
  347. }
  348.  
  349. }
  350.  
  351.  
  352. 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.
  353. if(myGameID == 0){
  354. myWeaponID = 1;
  355. maxAmmo = 30;
  356. ammo = 30;
  357. maxLife = 3;
  358. life = 3;
  359. myWeaponHP = 1;
  360. }
  361. if(myGameID == 1){
  362. myWeaponID = 1;
  363. maxAmmo = 100;
  364. ammo = 100;
  365. maxLife = 10;
  366. life = 10;
  367. myWeaponHP = 2;
  368. }
  369. }
  370.  
  371.  
  372. void frequencyCalculations() { // Works out all the timings needed to give the correct carrier frequency for the IR signal
  373. IRt = (int) (500/IRfrequency);
  374. IRpulses = (int) (IRpulse / (2*IRt));
  375. IRt = IRt - 4;
  376. // Why -4 I hear you cry. It allows for the time taken for commands to be executed.
  377. // More info: http://j44industries.blogspot.com/2009/09/arduino-frequency-generation.html#more
  378.  
  379. Serial.print("Oscilation time period /2: ");
  380. Serial.println(IRt);
  381. Serial.print("Pulses: ");
  382. Serial.println(IRpulses);
  383. timeOut = IRpulse + 50; // Adding 50 to the expected pulse time gives a little margin for error on the pulse read time out value
  384. }
  385.  
  386.  
  387. void tagCode() { // Works out what the players tagger code (the code that is transmitted when they shoot) is
  388. byte1[0] = myTeamID >> 2 & B1;
  389. byte1[1] = myTeamID >> 1 & B1;
  390. byte1[2] = myTeamID >> 0 & B1;
  391.  
  392. byte1[3] = myPlayerID >> 4 & B1;
  393. byte1[4] = myPlayerID >> 3 & B1;
  394. byte1[5] = myPlayerID >> 2 & B1;
  395. byte1[6] = myPlayerID >> 1 & B1;
  396. byte1[7] = myPlayerID >> 0 & B1;
  397.  
  398.  
  399. byte2[0] = myWeaponID >> 2 & B1;
  400. byte2[1] = myWeaponID >> 1 & B1;
  401. byte2[2] = myWeaponID >> 0 & B1;
  402.  
  403. byte2[3] = myWeaponHP >> 4 & B1;
  404. byte2[4] = myWeaponHP >> 3 & B1;
  405. byte2[5] = myWeaponHP >> 2 & B1;
  406. byte2[6] = myWeaponHP >> 1 & B1;
  407. byte2[7] = myWeaponHP >> 0 & B1;
  408.  
  409. myParity = 0;
  410. for (int i=0; i<8; i++) {
  411. if(byte1[i] == 1){myParity = myParity + 1;}
  412. if(byte2[i] == 1){myParity = myParity + 1;}
  413. myParity = myParity >> 0 & B1;
  414. }
  415.  
  416. // Next few lines print the full tagger code.
  417. Serial.print("Byte1: ");
  418. Serial.print(byte1[0]);
  419. Serial.print(byte1[1]);
  420. Serial.print(byte1[2]);
  421. Serial.print(byte1[3]);
  422. Serial.print(byte1[4]);
  423. Serial.print(byte1[5]);
  424. Serial.print(byte1[6]);
  425. Serial.print(byte1[7]);
  426. Serial.println();
  427.  
  428. Serial.print("Byte2: ");
  429. Serial.print(byte2[0]);
  430. Serial.print(byte2[1]);
  431. Serial.print(byte2[2]);
  432. Serial.print(byte2[3]);
  433. Serial.print(byte2[4]);
  434. Serial.print(byte2[5]);
  435. Serial.print(byte2[6]);
  436. Serial.print(byte2[7]);
  437. Serial.println();
  438.  
  439. Serial.print("Parity: ");
  440. Serial.print(myParity);
  441. Serial.println();
  442. }
  443.  
  444.  
  445. void playTone(int tone, int duration) { // A sub routine for playing tones like the standard arduino melody example
  446. for (long i = 0; i < duration * 1000L; i += tone * 2) {
  447. digitalWrite(speakerPin, HIGH);
  448. delayMicroseconds(tone);
  449. digitalWrite(speakerPin, LOW);
  450. delayMicroseconds(tone);
  451. }
  452. }
  453.  
  454.  
  455. void dead() { // void determines what the tagger does when it is out of lives
  456. // Makes a few noises and flashes some lights
  457. for (int i = 1;i < 254;i++) {
  458. analogWrite(ammoPin, i);
  459. playTone((1000+9*i), 2);
  460. }
  461. analogWrite(ammoPin, ((int) ammo));
  462. analogWrite(lifePin, ((int) life));
  463. Serial.println("DEAD");
  464.  
  465. for (int i=0; i<10; i++) {
  466. analogWrite(ammoPin, 255);
  467. digitalWrite(hitPin,HIGH);
  468. delay (500);
  469. analogWrite(ammoPin, 0);
  470. digitalWrite(hitPin,LOW);
  471. delay (500);
  472. }
  473. }
  474.  
  475.  
  476. void noAmmo() { // Make some noise and flash some lights when out of ammo
  477. digitalWrite(hitPin,HIGH);
  478. playTone(500, 100);
  479. playTone(1000, 100);
  480. digitalWrite(hitPin,LOW);
  481. }
  482.  
  483.  
  484. void hit() { // Make some noise and flash some lights when you get shot
  485. digitalWrite(hitPin,HIGH);
  486. life = life - hp[hitNo];
  487. Serial.print("Life: ");
  488. Serial.println(life);
  489. playTone(500, 500);
  490. if(life <= 0){dead();}
  491. digitalWrite(hitPin,LOW);
  492. lifeDisplay();
  493. }
Add Comment
Please, Sign In to add comment