Laser Tag C10 – Triggers

Love Jesus, Love Code

This tutorial will take approximately 45 minutes

Simple Trigger

To get the trigger button working, a simple flowchart was created to clarify the logic. However, the flowchart is much simpler than the final code. The code features various firing modes, different sound options, and accounts for whether the player is dead, none of which were included in the flowchart. Nevertheless, the flowchart served as a useful starting point.

Circuit Diagram

New Feature cycle

Save your project as “LaserTag_C10_Triggers”

Libraries

No additional libraries are required to add triggers. In the diagram above, one end of the button is connected to ground, while the other is connected to the pin. When the pin is grounded, it indicates that the button has been pressed.

The program will push the limits of the sound module. Depending on the specific sound module in use, you may need to add an additional line of code to the JQ6500 library. Modifying the JQ6500_Serial.cpp file is likely to have a positive effect on the JQ6500 module. Locate the library file and then add the line below.

Variables

Add the following variables

The variables triggerButton and previousTriggerState work together to identify changes in the trigger button’s state, such as when it is pressed or released. This is essential for the semi-automatic firing mode, ensuring that only one reload occurs at any given time.

Setup()

Add the function call “setupTriggers()”

New tab – Triggers

Add a new tab called Triggers and copy the following code

void setupTriggers(){
  pinMode(triggerPin, INPUT_PULLUP);
  pinMode(reloadPin, INPUT_PULLUP);
}

void triggers() { // Checks to see if the triggers have been pressed
  previousTriggerState = triggerButton;  // Stores previous trigger state
  triggerButton = digitalRead(triggerPin); // Looks up current trigger button state
  previousReloadState = reloadButton;
  reloadButton = digitalRead(reloadPin);
  if(triggerButton == LOW){ //trigger button pressed
    if (shotsLeft > 0 && shotReady()){  //checks for ammo and if enough time passed since last shot
      shoot();
    }
    else if(shotsLeft <= 0 && shotReady() && triggerButton != previousTriggerState){
      noAmmo();
    }
  }
  //replenish burstShots to original burstSize on triggerButton up
  if (triggerButton == HIGH){
    burstShots = burstSize;  
  }
  if(reloadButton == LOW && reloadButton != previousReloadState && !reloading){ 
    if (clips>0){
      reload();
    }
    else {
      noAmmo();
    }
  }
  if (reloading &&  millis()>shotDelay+shotDelayTime){ //reloading true and passed reload time
    switch (soundSet){ 
      case 0: mp3.playFileByIndexNumber(4); break; // military reload
      case 1: mp3.playFileByIndexNumber(22); break; // si-fi reload
    }
    reloading = false;
  } 
}

void shoot(){
  if (bitRead(muzFlash, 4)==1){flashOn();}

  //send out shot pulse over IR
  #if SEND_GLOBALCACHE
    irsend.sendSony(shotCode, 15, 0); // sends a 15 bit pulse, the shotCode is in decimal, the last digit creats a single shot
    debugInfo += "shotCode: " + String(shotCode, BIN) + " sent <br>";
  #else   // SEND_GLOBALCACHE
    debugInfo += "shotCode: " + String(shotCode, BIN) + " not sent because SEND_GLOBALCACHE not avaliable <br>"; 
  #endif  

  switch (soundSet){ 
    case 0: mp3.playFileByIndexNumber(1); break; // military shot
    case 1: mp3.playFileByIndexNumber(21); break; // si-fi shot
    } 
  teamColour(); 
  shotsLeft -= 1;
  shotDelayTime = millis();
}

void reload(){
  reloading = true;
  clips--; 
  shotsLeft = magSize; 
  shotDelayTime = millis() + reloadDelay*1000; 
  switch (soundSet){ 
    case 0: mp3.playFileByIndexNumber(4); break; // military reload
    case 1: mp3.playFileByIndexNumber(22); break; // si-fi reload
  }
}

void noAmmo(){
  mp3.playFileByIndexNumber(2);
}

bool shotReady(){
  // This function checks to see if enough time past since last shot (depends on shot mode)
  if (fireSelect == 0 && triggerButton != previousTriggerState){  //semi-auto
    return true;
  }
  else if (fireSelect == 1 && millis()>(shotDelay+shotDelayTime) && burstShots > 0){  //burst
    burstShots -= 1;
    return true;
  }
  else if (fireSelect == 2 && millis()>(shotDelay+shotDelayTime)){  //full auto
    return true;
  } 
  return false;
}

Code Explanation:
setupTriggers() – The pinMode function determines whether a pin is set to receive voltage or to be connected to ground. When the button is pressed, it connects the pin to ground. If the pin is already grounded, it behaves as if the button is constantly pressed. We will configure both the trigger and reload pins to the pull-up position.
triggers() checks whether the trigger button has been pressed. It first verifies if the button is pressed; if so, it stores the value “LOW”; otherwise, it retains the value “HIGH”. The values for LOW and HIGH can also be represented numerically: LOW = 0 and HIGH = 1.
shoot() activates the LED strip at full brightness, plays the shooting sound, and then returns the LED strip to the team’s colour.

Tab – Game tab

Testing

Pressing the fire button should now cause the LED lights to illuminate in bright white.

Button pressed – white colour at full strength
No button pressed – team colour showing

Testing different modes

Using the web interface, experiment with the various shot modes to confirm they are functioning correctly. The speakers are likely to handle a shot rate of only 250-400 rounds per minute, depending on the sound module. Additionally, be sure to test the sci-fi and military sound effects.

Christian Content

Timing in the game is crucial. We not only use timing to count down to the end of the game but also to manage the shot rate and the duration of reloads. When a player dies, the flashing of the LEDs on and off also relies on a timer.

Additionally, we benefit from audio and visual cues that indicate timing. The gun features a time display that shows how long is left in the game. There is also a second reload sound to signal when your gun is ready to fire, while the shot sound provides an indication of your shooting speed.

Timing is essential, especially when waiting for the next event to unfold.

The Bible contains some intriguing passages regarding timing, which we will examine today.

2 Peter 3:8–13 (HCSB)

8 Dear friends, don’t let this one thing escape you: With the Lord one day is like a thousand years, and a thousand years like one day. 9 The Lord does not delay His promise, as some understand delay, but is patient with you, not wanting any to perish but all to come to repentance. 10 But the Day of the Lord will come like a thief; on that day the heavens will pass away with a loud noise, the elements will burn and be dissolved, and the earth and the works on it will be disclosed. 11 Since all these things are to be destroyed in this way, it is clear what sort of people you should be in holy conduct and godliness 12 as you wait for and earnestly desire the coming of the day of God. The heavens will be on fire and be dissolved because of it, and the elements will melt with the heat. 13 But based on His promise, we wait for the new heavens and a new earth, where righteousness will dwell.

It is fascinating how the Bible discusses timing and the future of our world.

Unlike the game, however, we may find ourselves wondering when certain events will occur in the future.

Scientific theories predict that in a few billion years, our sun will enter its red giant phase, growing larger and eventually engulfing the Earth. Both the Bible and science suggest that our sky and Earth will melt with heat.

Yet, when our Earth is destroyed by fire, God provides a solution. He will create a new heaven and a new earth where the righteous will dwell. Unlike the laser tag game, we have no indication of when this will happen—there is no countdown clock or warning sound. Verse 10 states that this day will arrive unexpectedly, like a thief. The only way to be prepared for this day is to be righteous now, or, in other words, “right with God.” This rightness with God can only be achieved by trusting in and following Jesus.

Leave a Reply