January 21, 2016

ESP8266-based lasertag - alpha 0.1

So I have been working for a while on this project. My goal is to create a cheap lasertag solution, and they way I want to achieve it is to use ESP8266 modules communicating over wifi. Right now I'm in the middle of the development, but I have to order an other ESP8266 board, so I'm writing this article basically for me to be a reminder of what I did sofar. Although it might be useful for others too.

Here is my very first prototype:
As you can see I'm using an ESP8266-12 based development board with onboard voltage regulator and programmer, so I can program it via USB. I got it from ebay for $6.
I'm using the arduino ide to program it (for more see: https://github.com/esp8266/Arduino). For some reason the printed pin numbers on the board are not the same as I can access them from the code. So here is the mapping between the two:
Board - code
D1 - 5
D2 - 4
D3 - 0
D4 - 2
D5 - 14
D6 - 12
D7 - 13
D8 - 15
The board also has 2 built in led, a blue and a red available on pin 2 and 16 from the code.

I have a red laser diode connected to pin D5/14, an IR receiver on D2/4 and a trigger with pull down resistor on pin D6/12. The IR receiver is connected this way:
The code uses the IRremoteESP8266 library, which was partly ported to ESP8266 by me and available here: https://github.com/markszabo/IRremoteESP8266 With this library it's easy to shoot and detect if got shot. However I want to add feedback also for the killer (like some sound from a piezo buzzer or similar). This feature is the main reason why I'm using ESP8266 and not simply arduino. So when the victim got hit, his gun will send a message to the killer, to notify him. To do this I chose a very easy solution: each gun is serving a webserver on port 80. The code, what the guns are sending through IR is the last 3 digits of their IP address. So when the victim receives a hit, it decodes the IR code, then sends a simple HTTP GET request to that IP like GET /?killed=123, where 123 is the last number of the victims IP address. It seems to work, but right now I have only one working ESP8266, so I could not test this feature

And here is the code (right now it's a bit "spaghetti code", in the end I will write it better):

#include <IRremoteESP8266.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

//Pin config
int triggerPin = 12; //D6
int laserPin = 14; //D5
int receiverPin = 4; //D2
//Wifi config
const char* ssid = "MMaarrkk";
const char* password = "Iwilltellyoutomorrowat824";
//Misc config
int minimumTimeBetweenShoots = 500; //in millisecond

//Global variables

ESP8266WebServer server(80);

String lastNumberOfIP;

volatile int lastShootTime = 0;

IRrecv irrecv(receiverPin);
decode_results results;

IRsend irsend(laserPin);

int pin16state = false;
void killedSomeone() {
  // killedId = server.args(0);
  pin16state = !pin16state;
  digitalWrite(16, pin16state); //visualize it
  server.send(200, "text/plain", "1"); //answer with 1
void handleNotFound(){
  server.send(404, "text/plain", "404 error");

void setup() {
  irrecv.enableIRIn(); // Start the receiver
  pinMode(triggerPin, INPUT);
  pinMode(16, OUTPUT);
  //connect to wifi
  WiFi.begin(ssid, password);
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
  Serial.print("Connected to ");
  Serial.print("IP address: ");
  lastNumberOfIP = getLastNumberOfIP(WiFi.localIP().toString());
  //setup and start server
  server.on("/", killedSomeone);

void loop() {
  if (irrecv.decode(&results)) {
    Serial.print("Received value: ");
    irrecv.resume(); // Receive the next value
  if(digitalRead(triggerPin)) shoot();


void shoot()
  if(lastShootTime+500 < millis()){
    lastShootTime = millis();
    irsend.sendNEC(lastNumberOfIP.toInt(), 36);

void hit(int killer)
  // Use WiFiClient class to create TCP connections
  WiFiClient client;
  //const char* chKiller = "000";
  //const char* host = "192.168.0." + String(killer);
  String host = "192.168.0." + String(killer);
  const int httpPort = 80;
  if (!client.connect(host.c_str(), httpPort)) { //host: String -> const char * with c_str()
    Serial.println("connection failed");
  // We now create a URI for the request
  String url = "?killed=" + lastNumberOfIP;
  Serial.print("Requesting URL: ");
  // This will send the request to the server
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
  // Read all the lines of the reply from server and print them to Serial
    String line = client.readStringUntil('\r');

String getLastNumberOfIP(String ip)
  return ip.substring(ip.lastIndexOf('.')+1);


  1. I love this stuff...
    Thanks for posting it.
    Maybe you could add some code for controlling an LED strip so people can see who is hit/alive/dead and what team he is playing... I once made a laser tag project using a Nintendo DS lite and some diy IO on slot2...

  2. I love this stuff...
    Thanks for posting it.
    Maybe you could add some code for controlling an LED strip so people can see who is hit/alive/dead and what team he is playing... I once made a laser tag project using a Nintendo DS lite and some diy IO on slot2...

  3. Hi, would you be able to add the magiquest sections of this ir lib into yours?


    i want to do something similar to your laser tag but with magiquest wands

    1. Sorry, I don't really have time to work on this project right now, but you can simply copy everything starting with magiquest to the corresponding IRremoteInt.h IRremote.h IRremote.cpp files of my library and try to use it. It will probably fail, but based on the error messages, you can figure out which variable is missing. If you are done, and it's working, you can issue a pull request, and I will merge it to my library :)


  4. Someone Sometimes with visits your blog regularly and recommended it in my experience to read as well.