Infrared laser tag with two Arduino Nanos
Intermediate
Or see every lesson.
- 455parts on the bench
- 43boards running now
- 1.00xreal time, on every board
An infrared LED looks like any other LED with a clear or blue-tinted lens, and it is how every TV remote talks. Put one on an Arduino pin and the IRremote library can send the same codes a remote sends, to a VS1838B receiver on another board. That is all laser tag is: each player has an IR LED to shoot with and a receiver to be hit on.
The circuit above is two Arduino Nanos facing each other, each with a trigger button, an IR LED behind a 220 ohm resistor, a VS1838B receiver and a red hit LED. Press a trigger and that player fires one NEC code; the other player’s receiver decodes it, counts the hit and lights its LED. Five hits and you are out. Both boards run the same sketch.
What you need
- Two Arduino Nanos (or Unos)
- Two 940 nm IR LEDs, or two KY-005 modules, with a 220 ohm resistor each
- Two VS1838B IR receivers (or KY-022 modules)
- Two pushbuttons
- Two red LEDs with a 220 ohm resistor each
- A breadboard and jumper wires for each player
Wiring
| Part and pin | Goes to | Note |
|---|---|---|
| IR LED, through 220 ohm | Pin 3 | IrSender’s pin; the LED’s short leg to GND |
| VS1838B OUT | Pin 2 | IrReceiver.begin(2) |
| VS1838B VCC and GND | 5 V and GND | |
| Trigger button | Pin 4 and GND | INPUT_PULLUP, so pressed reads LOW |
| Hit LED, through 220 ohm | Pin 5 |
Why the LED has to flash at 38 kHz
A VS1838B does not answer infrared light. Sunlight and lamps are full of it. It answers light that flashes on and off about 38,000 times a second, and turns each burst of that into a LOW on its OUT pin. So an IR LED that is simply switched on with digitalWrite() lights up (a phone camera shows it glowing violet) and the receiver sees nothing at all.
IrSender does the flashing for you. Every burst of a frame, it switches the pin on and off at 38 kHz in software, on any pin you give it. In the simulator you can see the difference: drive the LED with digitalWrite(3, HIGH) and the receiver’s dome stays dark; send with IrSender and it lights for every burst.
One shot, one NEC frame
A shot is an NEC frame with address 0x1A and command 0x01, which takes about 70 milliseconds to send. The receiving player checks all three before counting a hit: the protocol, the address and the command. Anything else in the room, a TV remote for instance, is ignored.
The trigger fires on the moment the button goes from HIGH to LOW, and then the gun reloads for half a second. Holding the button fires once, and the few milliseconds of contact bounce never fire twice.
#define DECODE_NEC
#include <IRremote.hpp>
void loop() {
unsigned long now = millis();
bool pressed = digitalRead(TRIGGER_PIN) == LOW;
if (pressed && !wasPressed && now >= reloadedAt && hits < LIVES) {
reloadedAt = now + RELOAD_MS;
IrSender.sendNEC(TAG_ADDRESS, SHOT, 0);
Serial.println(F("Fire!"));
}
wasPressed = pressed;
if (IrReceiver.decode()) {
IRData &shot = IrReceiver.decodedIRData;
bool repeat = shot.flags & IRDATA_FLAGS_IS_REPEAT;
if (shot.protocol == NEC && shot.address == TAG_ADDRESS &&
shot.command == SHOT && !repeat && hits < LIVES) {
hits++;
hitUntil = now + HIT_MS;
Serial.print(F("Hit! "));
Serial.print(hits);
Serial.print(F(" of "));
Serial.println(LIVES);
}
IrReceiver.resume();
}
digitalWrite(HIT_PIN, now < hitUntil || hits >= LIVES ? HIGH : LOW);
}Aiming
Infrared goes in a straight line. A 5 mm IR LED throws a beam about 20 degrees either side of straight ahead, and a VS1838B sees light that arrives within about 45 degrees of where its lens faces. Each player’s LED and receiver point the same way, at the other player, so you can never tag yourself.
Mokxi works the beam out from the canvas: the LED’s rotation, its aim setting, its range (the canvas is to scale, and the default reaches about 5 m) and anything in the way. Hover over an IR LED to see its beam. Turn one away while the circuit runs and its shots stop landing; put a part on the line between the two players and nothing gets through.
Try it in the editor
Rotate the left player’s IR LED, or set its aim to 60, and fire: the serial monitor says "Fire!" and the other player hears nothing. Set it back to 0 and the next shot is a hit.
Copy one player’s parts and wires, with a third Nano, and you have three-way laser tag. Every receiver a LED is pointed at gets the shot.
Give each player a different TAG_ADDRESS and have the receiver print who hit it: decodedIRData.address is the shooter.
Common mistakes
Turning the IR LED on with digitalWrite(). It lights, but a receiver ignores anything that is not flashing near 38 kHz.
No resistor on the IR LED. Its drop is only about 1.2 V, so a pin straight into it tries to pass far more than the pin’s 40 mA limit.
Calling tone() on a board that is also receiving. IrReceiver uses timer 2 on a Nano, and so does tone().
Expecting to see the LED light up. Infrared is invisible to the eye; look at it through a phone camera.
Questions
Which pin does IrSender use?
Any pin: IrSender.begin(pin) makes the carrier in software on the pin you give it. Older sketches that call IrSender.begin() with no pin use IR_SEND_PIN, which is 3 on a Nano or Uno.
Do I need a KY-005 module or a bare LED?
Either. The KY-005 is the same 940 nm LED on a small board, from S to the - pin, and most boards carry no resistor, so add one as you would for a bare LED.
How far does an IR LED reach?
A few meters indoors from a 20 mA LED, more from a remote that pulses its LED harder. Mokxi gives each LED a range you can set, 5 m by default, and anything in range and in the beam is seen.
Keep going
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.