# Lab 33: IR Battle Game
## **Objective**
Create a two-player IR battle game where players use "guns" to shoot IR signals at "targets". The gun sends IR signals when the button is pressed, and the target receives signals to reduce lives. Each player has 5 lives represented by WS2812 LEDs, with sound effects for shooting, hits, and game over.
## **Required Components**
### **Gun (Arduino #1)**
1. **Lonely Binary UNO R3** - Main Arduino board
2. **TinkerBlock UNO R3 Shield** - Expansion shield that plugs onto the UNO R3
3. **TinkerBlock TK04** - Push Button Module (trigger)
4. **TinkerBlock TK36** - Active Buzzer Module (shooting sound)
5. **TinkerBlock TK63** - Infrared Transmitter Module (bullet)
### **Target (Arduino #2)**
1. **Lonely Binary UNO R3** - Main Arduino board
2. **TinkerBlock UNO R3 Shield** - Expansion shield that plugs onto the UNO R3
3. **TinkerBlock TK33** - WS2812 LED Strip Module (life indicator)
4. **TinkerBlock TK37** - Passive Buzzer Module (hit/death sounds)
5. **TinkerBlock TK64** - Infrared Receiver Module (bullet detection)
## **Theory**
### **IR Battle Game Mechanics**
- **Gun System**: Button press triggers IR signal transmission
- **Target System**: IR receiver detects incoming "bullets"
- **Life System**: 5 lives represented by WS2812 LEDs
- **Sound Effects**: Different sounds for shooting, hits, and death
### **Game Flow**
- **Shooting**: Press button → Play shooting sound → Send IR signal
- **Hit Detection**: Receive IR signal → Reduce life → Play hit sound
- **Life Management**: Each hit reduces one LED
- **Game Over**: All 5 lives lost → Play death sound → Game reset
### **IR Communication Protocol**
- **Shoot Signal**: Specific IR pattern for bullet transmission
- **Hit Confirmation**: Target acknowledges successful hit
- **Game State**: Synchronized game state between players
## **Wiring Instructions**
### **Gun Arduino Wiring**
#### **TK04 Push Button Pinout**
- **GND** → Arduino GND
- **VCC** → Arduino 5V
- **NC** → No Connection
- **Signal** → Arduino Digital Pin D5
#### **TK36 Active Buzzer Pinout**
- **GND** → Arduino GND
- **VCC** → Arduino 5V
- **NC** → No Connection
- **Signal** → Arduino Digital Pin D9
#### **TK63 Infrared Transmitter Pinout**
- **GND** → Arduino GND
- **VCC** → Arduino 5V
- **NC** → No Connection
- **Signal** → Arduino Digital Pin D7
#### **Gun Connection Diagram**
```
Gun UNO R3 + Shield
├── 5V ──────────────→ TK04 VCC, TK36 VCC, TK63 VCC
├── GND ─────────────→ TK04 GND, TK36 GND, TK63 GND
├── Digital Pin D5 ──→ TK04 Signal (Trigger)
├── Digital Pin D7 ──→ TK63 Signal (IR Transmitter)
└── Digital Pin D9 ──→ TK36 Signal (Shooting Sound)
```
### **Target Arduino Wiring**
#### **TK33 WS2812 LED Strip Pinout**
- **GND** → Arduino GND
- **VCC** → Arduino 5V
- **NC** → No Connection
- **Signal** → Arduino Digital Pin D5
#### **TK37 Passive Buzzer Pinout**
- **GND** → Arduino GND
- **VCC** → Arduino 5V
- **NC** → No Connection
- **Signal** → Arduino Digital Pin D7
#### **TK64 Infrared Receiver Pinout**
- **GND** → Arduino GND
- **VCC** → Arduino 5V
- **NC** → No Connection
- **Signal** → Arduino Digital Pin D3
#### **Target Connection Diagram**
```
Target UNO R3 + Shield
├── 5V ──────────────→ TK33 VCC, TK37 VCC, TK64 VCC
├── GND ─────────────→ TK33 GND, TK37 GND, TK64 GND
├── Digital Pin D3 ──→ TK64 Signal (IR Receiver)
├── Digital Pin D5 ──→ TK33 Signal (WS2812 Data)
└── Digital Pin D7 ──→ TK37 Signal (Hit/Death Sounds)
```
## **Gun Code (Arduino #1)**
```cpp
// Gun Code - IR Battle Game
// Upload this code to the GUN Arduino
#include <FastLED.h>
// Pin definitions
#define BUTTON_PIN 5 // TK04 Push Button on D5
#define IR_TX_PIN 7 // TK63 Infrared Transmitter on D7
#define BUZZER_PIN 9 // TK36 Active Buzzer on D9
// Game parameters
#define MAX_SHOTS 10 // Maximum shots per game
#define SHOT_COOLDOWN 500 // Cooldown between shots (ms)
#define IR_PULSE_COUNT 5 // Number of IR pulses per shot
// Variables
bool buttonPressed = false;
bool lastButtonState = false;
unsigned long lastShotTime = 0;
int shotsFired = 0;
int hitsConfirmed = 0;
bool gameActive = true;
void setup() {
// Initialize serial communication
Serial.begin(9600);
Serial.println("IR Battle Game - GUN");
Serial.println("====================");
// Initialize pins
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(IR_TX_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
// Start with IR transmitter off
digitalWrite(IR_TX_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
delay(1000);
Serial.println("Gun initialized!");
Serial.println("TK04 Button: D5 (Trigger)");
Serial.println("TK63 IR Transmitter: D7 (Bullet)");
Serial.println("TK36 Buzzer: D9 (Shooting Sound)");
Serial.println();
Serial.println("Press button to shoot!");
Serial.println("Game starts in 3 seconds...");
Serial.println();
// Countdown to game start
for (int i = 3; i > 0; i--) {
Serial.print("Game starts in: ");
Serial.println(i);
delay(1000);
}
Serial.println("GAME START! Fire at will!");
Serial.println();
}
void loop() {
// Read button state
bool currentButtonState = !digitalRead(BUTTON_PIN);
// Handle button press (rising edge)
if (currentButtonState && !lastButtonState) {
buttonPressed = true;
handleShoot();
}
// Handle button release (falling edge)
if (!currentButtonState && lastButtonState) {
buttonPressed = false;
}
// Update button state
lastButtonState = currentButtonState;
// Check for game end
if (shotsFired >= MAX_SHOTS && gameActive) {
endGame();
}
delay(10);
}
void handleShoot() {
// Check cooldown
if (millis() - lastShotTime < SHOT_COOLDOWN) {
Serial.println("Cooldown! Wait before next shot.");
return;
}
// Check if game is still active
if (!gameActive) {
Serial.println("Game over! Reset to play again.");
return;
}
// Fire the shot
fireShot();
// Update shot counter
shotsFired++;
lastShotTime = millis();
// Display shot info
Serial.print("Shot #");
Serial.print(shotsFired);
Serial.print(" fired! Shots remaining: ");
Serial.println(MAX_SHOTS - shotsFired);
}
void fireShot() {
// Play shooting sound
playShootingSound();
// Send IR bullet signal
sendIRBullet();
Serial.println("BANG! Bullet fired!");
}
void playShootingSound() {
// Shooting sound effect
tone(BUZZER_PIN, 800, 100); // High pitch "bang"
delay(100);
tone(BUZZER_PIN, 600, 50); // Lower pitch "recoil"
delay(50);
noTone(BUZZER_PIN);
}
void sendIRBullet() {
// Send IR bullet pattern
// This creates a distinctive IR signal for bullet detection
for (int i = 0; i < IR_PULSE_COUNT; i++) {
// Send IR pulse
digitalWrite(IR_TX_PIN, HIGH);
delayMicroseconds(1000); // 1ms pulse
digitalWrite(IR_TX_PIN, LOW);
delayMicroseconds(1000); // 1ms gap
}
// Send end marker
digitalWrite(IR_TX_PIN, HIGH);
delayMicroseconds(5000); // 5ms end marker
digitalWrite(IR_TX_PIN, LOW);
}
void endGame() {
gameActive = false;
Serial.println("=== GAME OVER ===");
Serial.print("Total shots fired: ");
Serial.println(shotsFired);
Serial.print("Hits confirmed: ");
Serial.println(hitsConfirmed);
Serial.print("Accuracy: ");
if (shotsFired > 0) {
Serial.print((hitsConfirmed * 100) / shotsFired);
Serial.println("%");
} else {
Serial.println("0%");
}
Serial.println();
Serial.println("Press button to reset game...");
// Play game over sound
playGameOverSound();
}
void playGameOverSound() {
// Game over sound effect
tone(BUZZER_PIN, 400, 200);
delay(200);
tone(BUZZER_PIN, 300, 200);
delay(200);
tone(BUZZER_PIN, 200, 400);
delay(400);
noTone(BUZZER_PIN);
}
void resetGame() {
shotsFired = 0;
hitsConfirmed = 0;
gameActive = true;
lastShotTime = 0;
Serial.println("Game reset! Ready to shoot!");
Serial.println();
}
// Function to receive hit confirmation (if using two-way communication)
void receiveHitConfirmation() {
// This would be used if implementing two-way IR communication
hitsConfirmed++;
Serial.print("Hit confirmed! Total hits: ");
Serial.println(hitsConfirmed);
}
```
## **Target Code (Arduino #2)**
```cpp
// Target Code - IR Battle Game
// Upload this code to the TARGET Arduino
#include <FastLED.h>
// Pin definitions
#define LED_PIN 5 // TK33 WS2812 LED Strip on D5
#define BUZZER_PIN 7 // TK37 Passive Buzzer on D7
#define IR_RX_PIN 3 // TK64 Infrared Receiver on D3
#define NUM_LEDS 5 // TK33 has 5 WS2812 LEDs
// LED array
CRGB leds[NUM_LEDS];
// Game parameters
#define MAX_LIVES 5 // Maximum lives per game
#define HIT_DETECTION_TIME 100 // Time to detect IR signal (ms)
#define DEBOUNCE_TIME 200 // Debounce time between hits (ms)
// Variables
int currentLives = MAX_LIVES;
bool irSignalDetected = false;
bool lastIRSignal = false;
unsigned long lastHitTime = 0;
int hitsReceived = 0;
bool gameActive = true;
bool targetDestroyed = false;
void setup() {
// Initialize serial communication
Serial.begin(9600);
Serial.println("IR Battle Game - TARGET");
Serial.println("=======================");
// Initialize WS2812 LED strip
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(100);
// Initialize pins
pinMode(IR_RX_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
// Initialize LEDs to show full lives
initializeLives();
delay(1000);
Serial.println("Target initialized!");
Serial.println("TK33 WS2812 LED Strip: D5 (Life Indicator)");
Serial.println("TK37 Passive Buzzer: D7 (Hit/Death Sounds)");
Serial.println("TK64 IR Receiver: D3 (Bullet Detection)");
Serial.println();
Serial.println("Waiting for incoming fire...");
Serial.println("Game starts in 3 seconds...");
Serial.println();
// Countdown to game start
for (int i = 3; i > 0; i--) {
Serial.print("Game starts in: ");
Serial.println(i);
delay(1000);
}
Serial.println("TARGET ACTIVE! Incoming fire expected!");
Serial.println();
}
void loop() {
// Read IR receiver
bool currentIRSignal = digitalRead(IR_RX_PIN);
// Handle IR signal detection (active high)
if (currentIRSignal && !lastIRSignal) {
// IR signal started
irSignalDetected = true;
handleIncomingFire();
}
else if (!currentIRSignal && lastIRSignal) {
// IR signal ended
irSignalDetected = false;
}
// Update IR signal state
lastIRSignal = currentIRSignal;
// Check for game over
if (currentLives <= 0 && gameActive) {
targetDestroyed = true;
gameOver();
}
delay(10);
}
void handleIncomingFire() {
// Check debounce time
if (millis() - lastHitTime < DEBOUNCE_TIME) {
return;
}
// Check if game is still active
if (!gameActive) {
return;
}
// Process the hit
processHit();
// Update hit counter
hitsReceived++;
lastHitTime = millis();
Serial.print("HIT! Lives remaining: ");
Serial.println(currentLives);
}
void processHit() {
// Reduce life
currentLives--;
// Update LED display
updateLifeDisplay();
// Play hit sound
playHitSound();
// Check if target is destroyed
if (currentLives <= 0) {
playDeathSound();
}
}
void initializeLives() {
// Set all LEDs to green (full lives)
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Green;
}
FastLED.show();
Serial.print("Lives initialized: ");
Serial.print(currentLives);
Serial.println(" / 5");
}
void updateLifeDisplay() {
// Clear all LEDs
FastLED.clear();
// Set remaining lives to green
for (int i = 0; i < currentLives; i++) {
leds[i] = CRGB::Green;
}
// Set lost lives to red
for (int i = currentLives; i < NUM_LEDS; i++) {
leds[i] = CRGB::Red;
}
FastLED.show();
Serial.print("Life display updated: ");
Serial.print(currentLives);
Serial.println(" lives remaining");
}
void playHitSound() {
// Hit sound effect
tone(BUZZER_PIN, 600, 100); // Medium pitch "hit"
delay(100);
tone(BUZZER_PIN, 400, 100); // Lower pitch "damage"
delay(100);
noTone(BUZZER_PIN);
}
void playDeathSound() {
// Death sound effect
Serial.println("*** TARGET DESTROYED! ***");
// Dramatic death sound
tone(BUZZER_PIN, 800, 200);
delay(200);
tone(BUZZER_PIN, 600, 200);
delay(200);
tone(BUZZER_PIN, 400, 200);
delay(200);
tone(BUZZER_PIN, 200, 400);
delay(400);
noTone(BUZZER_PIN);
// Flash all LEDs red
for (int i = 0; i < 5; i++) {
for (int j = 0; j < NUM_LEDS; j++) {
leds[j] = CRGB::Red;
}
FastLED.show();
delay(200);
FastLED.clear();
FastLED.show();
delay(200);
}
}
void gameOver() {
gameActive = false;
Serial.println("=== TARGET DESTROYED ===");
Serial.print("Total hits received: ");
Serial.println(hitsReceived);
Serial.print("Survival time: ");
Serial.print((millis() / 1000));
Serial.println(" seconds");
Serial.println();
Serial.println("Target destroyed! Game over!");
Serial.println("Reset Arduino to play again...");
// Keep death animation running
while (true) {
// Continuous red flash
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Red;
}
FastLED.show();
delay(500);
FastLED.clear();
FastLED.show();
delay(500);
}
}
// Function to send hit confirmation (if using two-way communication)
void sendHitConfirmation() {
// This would be used if implementing two-way IR communication
// to confirm hits back to the gun
Serial.println("Hit confirmation sent to gun");
}
```
## **Advanced Two-Way Battle Game**
```cpp
// Advanced Two-Way Battle Game
// Both Arduinos can be gun and target simultaneously
#include <FastLED.h>
// Pin definitions
#define BUTTON_PIN 5 // TK04 Push Button (Gun mode)
#define LED_PIN 5 // TK33 WS2812 LED Strip (Target mode)
#define IR_TX_PIN 7 // TK63 Infrared Transmitter (Gun mode)
#define IR_RX_PIN 3 // TK64 Infrared Receiver (Target mode)
#define BUZZER_PIN 9 // TK36 Active Buzzer (Gun mode)
#define PASSIVE_BUZZER_PIN 7 // TK37 Passive Buzzer (Target mode)
#define NUM_LEDS 5 // TK33 has 5 WS2812 LEDs
// LED array
CRGB leds[NUM_LEDS];
// Game parameters
#define MAX_LIVES 5
#define MAX_SHOTS 10
#define SHOT_COOLDOWN 500
#define HIT_DEBOUNCE 200
// Game states
enum GameState {
STATE_WAITING,
STATE_SHOOTING,
STATE_HIT,
STATE_GAME_OVER
};
// Variables
GameState currentState = STATE_WAITING;
int lives = MAX_LIVES;
int shotsFired = 0;
int hitsScored = 0;
bool buttonPressed = false;
bool lastButtonState = false;
bool irSignalDetected = false;
bool lastIRSignal = false;
unsigned long lastShotTime = 0;
unsigned long lastHitTime = 0;
bool gameActive = true;
void setup() {
Serial.begin(9600);
Serial.println("Advanced IR Battle Game");
Serial.println("=======================");
// Initialize pins
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(IR_TX_PIN, OUTPUT);
pinMode(IR_RX_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
// Initialize WS2812 LED strip
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(100);
// Initialize LEDs
initializeLives();
delay(1000);
Serial.println("Advanced battle system initialized!");
Serial.println("Press button to shoot, receive hits to lose lives");
Serial.println("Game starts in 3 seconds...");
Serial.println();
// Countdown
for (int i = 3; i > 0; i--) {
Serial.print("Game starts in: ");
Serial.println(i);
delay(1000);
}
Serial.println("BATTLE START!");
Serial.println();
}
void loop() {
// Read inputs
bool currentButtonState = !digitalRead(BUTTON_PIN);
bool currentIRSignal = digitalRead(IR_RX_PIN);
// Handle button press (shooting)
if (currentButtonState && !lastButtonState) {
handleShoot();
}
// Handle IR signal detection (being hit)
if (currentIRSignal && !lastIRSignal) {
handleHit();
}
// Update states
lastButtonState = currentButtonState;
lastIRSignal = currentIRSignal;
// Check game over conditions
if (lives <= 0 || shotsFired >= MAX_SHOTS) {
if (gameActive) {
gameOver();
}
}
delay(10);
}
void handleShoot() {
if (!gameActive || millis() - lastShotTime < SHOT_COOLDOWN) {
return;
}
// Fire shot
fireShot();
shotsFired++;
lastShotTime = millis();
Serial.print("Shot fired! Shots: ");
Serial.print(shotsFired);
Serial.print("/");
Serial.print(MAX_SHOTS);
Serial.print(" | Lives: ");
Serial.print(lives);
Serial.println("/5");
}
void handleHit() {
if (!gameActive || millis() - lastHitTime < HIT_DEBOUNCE) {
return;
}
// Process hit
lives--;
hitsScored++;
lastHitTime = millis();
updateLifeDisplay();
playHitSound();
Serial.print("HIT! Lives: ");
Serial.print(lives);
Serial.println("/5");
if (lives <= 0) {
playDeathSound();
}
}
void fireShot() {
// Shooting sound
tone(BUZZER_PIN, 800, 100);
delay(100);
tone(BUZZER_PIN, 600, 50);
delay(50);
noTone(BUZZER_PIN);
// Send IR bullet
for (int i = 0; i < 5; i++) {
digitalWrite(IR_TX_PIN, HIGH);
delayMicroseconds(1000);
digitalWrite(IR_TX_PIN, LOW);
delayMicroseconds(1000);
}
digitalWrite(IR_TX_PIN, HIGH);
delayMicroseconds(5000);
digitalWrite(IR_TX_PIN, LOW);
}
void initializeLives() {
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Green;
}
FastLED.show();
}
void updateLifeDisplay() {
FastLED.clear();
for (int i = 0; i < lives; i++) {
leds[i] = CRGB::Green;
}
for (int i = lives; i < NUM_LEDS; i++) {
leds[i] = CRGB::Red;
}
FastLED.show();
}
void playHitSound() {
tone(BUZZER_PIN, 600, 100);
delay(100);
tone(BUZZER_PIN, 400, 100);
delay(100);
noTone(BUZZER_PIN);
}
void playDeathSound() {
tone(BUZZER_PIN, 800, 200);
delay(200);
tone(BUZZER_PIN, 600, 200);
delay(200);
tone(BUZZER_PIN, 400, 200);
delay(200);
tone(BUZZER_PIN, 200, 400);
delay(400);
noTone(BUZZER_PIN);
}
void gameOver() {
gameActive = false;
Serial.println("=== GAME OVER ===");
Serial.print("Shots fired: ");
Serial.println(shotsFired);
Serial.print("Hits scored: ");
Serial.println(hitsScored);
Serial.print("Lives remaining: ");
Serial.println(lives);
if (lives <= 0) {
Serial.println("YOU WERE DESTROYED!");
} else {
Serial.println("OUT OF AMMO!");
}
// Death animation
for (int i = 0; i < 10; i++) {
for (int j = 0; j < NUM_LEDS; j++) {
leds[j] = CRGB::Red;
}
FastLED.show();
delay(200);
FastLED.clear();
FastLED.show();
delay(200);
}
}
```
## **Code Explanation**
### **Gun System Features**
- **Button Trigger**: Press button to fire shots
- **Shooting Sound**: Active buzzer plays shooting sound effects
- **IR Transmission**: Sends IR signals as "bullets"
- **Shot Cooldown**: Prevents rapid-fire shooting
- **Shot Counter**: Tracks total shots fired
### **Target System Features**
- **IR Detection**: Receives IR signals as incoming bullets
- **Life Management**: 5 lives represented by WS2812 LEDs
- **Hit Sounds**: Passive buzzer plays hit and death sounds
- **Visual Feedback**: LED strip shows remaining lives
- **Game Over**: Death animation when all lives lost
### **Game Mechanics**
- **Life System**: Each hit reduces one life (LED)
- **Sound Effects**: Different sounds for shooting, hits, and death
- **Visual Feedback**: LED colors indicate life status
- **Game States**: Waiting, shooting, hit, game over states
### **IR Communication**
- **Bullet Signal**: Specific IR pattern for bullet transmission
- **Hit Detection**: IR receiver detects incoming signals
- **Debouncing**: Prevents multiple hits from single shot
- **Range**: IR communication works within line of sight
## **Troubleshooting**
### **IR Communication Issues:**
- Check TK63/TK64 wiring and alignment
- Verify line of sight between modules
- Test with different distances
- Check for ambient IR interference
### **Button Issues:**
- Check TK04 wiring (D5 pin)
- Verify pull-up resistor configuration
- Test with simple digitalRead
- Check for loose connections
### **LED Strip Issues:**
- Check TK33 wiring (D5 pin)
- Verify power supply (5V, sufficient current)
- Test with simple color setting
- Check FastLED library installation
### **Buzzer Issues:**
- Check TK36/TK37 wiring
- Verify power connections
- Test with simple tone() function
- Check for pin conflicts
## **Applications**
### **Gaming**
- **Laser Tag**: IR-based combat games
- **Target Practice**: Shooting range simulation
- **Battle Games**: Multi-player combat systems
- **Training**: Military/police training systems
### **Educational**
- **IR Communication**: Learn about wireless communication
- **Game Design**: Practice game mechanics programming
- **Sensor Integration**: Learn about sensor networks
- **Real-time Systems**: Practice responsive system design
### **Entertainment**
- **Party Games**: Interactive entertainment systems
- **Theme Parks**: Interactive attractions
- **Arcade Games**: Custom arcade systems
- **Team Building**: Group activity systems
### **Prototyping**
- **Proof of Concept**: Test IR communication ideas
- **Sensor Networks**: Build multi-node systems
- **Game Mechanics**: Develop interactive systems
- **Wireless Systems**: Practice wireless communication
## **Next Steps**
- Add multiple players
- Implement different weapon types
- Create power-ups and special effects
- Add wireless score tracking
## **Resources**
- **IR Communication**: Infrared communication principles
- **Game Design**: Interactive game mechanics
- **FastLED Library**: WS2812 LED control
- **Real-time Systems**: Responsive system design
TinkerBlock UNO R3 Starter Kit
Dive into the world of electronics, Arduino programming, and STEM projects with the Lonely Binary TinkerBlock Series Starter Kit.
- Choosing a selection results in a full page refresh.