Kit of the Year Awards
Kit of the Year Awards!
January 7, 2020
Glue Gun Tips Image
Glue Gun Tips for Makers
January 15, 2020

Make a shooting gallery with up to 250 targets

 
 
I plan to flesh this out into a more complete free "Intermediate Arduino" course, but in the mean time, here are the sketches (which are thoroughly commented) and the Node Red sketch. You can get all the latest code for all of my AnotherMaker projects here.

ESP32 Laser Targets

Whether you make 2 or 250 targets, the system will auto-determine how many you have and trigger them randomly. Each target just needs a few configuration options by default (an id, a name, and 2 mp3s you want it to trigger when triggered and hit).

There is also room for functions to generate actions like additional LED lights or even physical actions like actuators. Functions are fired on activate, hit, and miss.

Please note: You do not need to actually use lasers for these targets. Arcade buttons would work fine for Nerf guns or other uses. Even steel plates in front of arcade buttons could work for things like pellet guns. Get creative! The point is not lasers...it is making flexible targets.

 

Front and Back view of an ESP32 Target

targets
 

ESP32 Code

// NOTE:
// GPIO
// 6-11 are used in the flash process and shouldn't be used.
// 34-39 are input only

//Special thanks to these 3 Youtubers who kept me sane when I had several weird issues on this project
//Simple Electronics - https://www.youtube.com/channel/UC3yasGCIt1rmKnZ8PukocSw
//Gadget Reboot - https://www.youtube.com/channel/UCwiKHTegfDe33K5wnmyULog
//Pile of Stuff - https://www.youtube.com/user/pileofstuff


#include "WiFi.h"
#include <PubSubClient.h>

//Initial configuration
#define DEVICE "cowboy" //This MUST be unique
char* mp3 = "cowboy1.mp3|cowboy2.mp3"; //The mp3 when activated then when hit
int id = 61;       //serves as the final ip address and unique identifier
int subnet = 95;   //the second to last set of numbers in your ip addresses 192.168.SUBNET.xxx
int serverip = 148;       //what is the last part of the ip address of your nodered/mqtt server 192.168.SUBNET.SERVERIP
const char* ssid = "YOUR_SSID_HERE";
const char* password = "YOUR_WIFI_PASSWORD_HERE";

//IO Variables
int powerLed = 25;    //green
int shootMeLed = 26;  //yellow
int hitLed = 27;      //red
int sensor = 14;      //laser sensor

//State Variables
int active = 0;                 //By default, should not accept input
int reading = 0;                //Store your sensor readinging
char me[10];                    //my id to be used in mqtt messages
char mp3me[32];                 //my mp3 request for setup
char avme[32];                  //activation request topic

//Networking / MQTT Variables that usually don't need to be changed
WiFiClient ethClient;
PubSubClient client(ethClient);
char *cstring;
IPAddress arduino_ip ( 192,  168,   subnet,  id);
IPAddress dns_ip     (  8,   8,   8,   8);
IPAddress gateway_ip ( 192,  168,   subnet,   1);
IPAddress subnet_mask(255, 255, 255,   0);
IPAddress server(192, 168, subnet, serverip);
int request = 0;


//GamePlay functions
void activate(unsigned long duration){
  unsigned long currentMillis = millis();
  Serial.println("Looking for input");
  //it's not obvious, but this next line will NOT keep looping. Only the stuff in the while loop will.
  unsigned long stopAt = currentMillis + duration;

  while(active == 1){
  currentMillis = millis();
  if (currentMillis <= stopAt) {
    //tell the world you're active
    digitalWrite(shootMeLed, HIGH);

    //read the sensor
    reading = digitalRead(sensor);
    if(reading == HIGH){
        iveBeenHit();
    }
  }else{
    //time is up!
    deactivate();
    client.publish("shooting/miss", "1");
    miss();
  }
  }
}

void deactivate(){
  //stop looking for input and go dark
  digitalWrite(shootMeLed, LOW);
  active = 0;
  Serial.println("Deactivating sensor");
}

void iveBeenHit(){
  deactivate();
  client.publish("shooting/hit", me);
  Serial.println("I have been hit!!!");
}

void miss(){
  //doing nothing right now
}

void callback(char* topic, byte* payload, unsigned int length) {
  for (int i = 0; i < length; i++) {
    payload[length] = '\0';
    cstring = (char *) payload;
  }

  request = atof(cstring);
  Serial.println("msg rec");
  if (strstr(topic, "whodere") != NULL) {
    request = atof(cstring);
    Serial.println("The request is...");
    Serial.println(request);
    if(request == 1){ //tell the server you're there so you can be added to the target list
      // sprintf(msg,"%d",id);
      Serial.println("WhoDere Request Received....responding.");
      client.publish("shooting/response", mp3me);
    }

  }
  if (strstr(topic, "activate") != NULL) {
    request = atof(cstring);
    Serial.println("An activation request has been sent out");
    Serial.println("I have been requested to activate for ");
    unsigned long dur = request;
    Serial.print(dur);
    Serial.println(" milliseconds");
    active = 1;
    activate(dur);
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(DEVICE)) {
      Serial.println("connected");
      // ... and resubscribe
      client.subscribe("shooting/whodere/#");
      client.subscribe(avme);
      // client.subscribe(subscription.c_str());
      // client.subscribe(reset.c_str());

    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 20 seconds");
      // Wait 20 seconds before retrying
      delay(20000);
    }
  }
}

void setup()
{

  sprintf(me,"%d",id);
  sprintf(avme,"shooting/activate%d/#",id);
  sprintf(mp3me,"%d|%s",id,mp3);
  Serial.begin(57600);
  Serial.print("My id is ");
  Serial.println(id);
  Serial.print("My activation topic is ");
  Serial.println(avme);
  //establish mqtt connections
  client.setServer(server, 1883);
  client.setCallback(callback);

  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.config(arduino_ip, gateway_ip, subnet_mask);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  // Allow the hardware to sort itself out
  pinMode(powerLed,OUTPUT);
  pinMode(shootMeLed,OUTPUT);
  pinMode(hitLed,OUTPUT);
  pinMode(sensor,INPUT);
  delay(1500);
}


void loop()
{
  if (!client.connected()) {
    reconnect();
      }
  client.loop();
  digitalWrite(powerLed,HIGH);
}

ESP8266 Laser Marker

This laser marker is just one option for shooting the targets. If you want to be able to differentiate between markers, you could use infrared or if you use something like Nerf, you don't need to make this at all.

 
Laser Marker
 

ESP8266 Code

/*
Do whatever you want with this, but BE CAREFUL.  I'm not responsible. You'll shoot someone's eye out.
This is built on a Wemos D1 Mini
Since the Wemos output pins are 3v, I'm using a transistor to switch the 5v on the board on/off to fire the laser
Nearly any NPN Transistor would work, but I'm using a 2n222
When you're looking at the "flat" face of the transistor...

Left(1) - Emittor
Center(2) - Base
Right(3) - Collector
The wiring goes like this...
1. 5v pin on the wemos to the +(S) side of the laser
2. - Side of the laser to the Collector (right) side of the transistor.
3. Both one wire of the trigger AND the Emittor (left) side of the trasnistor go to Wemos Ground
4. The Base (center) of the transistor goes to pin D2 (AKA GPIO 4) on the Wemos
5. Pin D1 (AKA GPIO 5) goes to the other side of the pushbutton

*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>


#define DEVICE "PewPew"   //This MUST be unique
int laser = 4;            //pin d2
int button = 5;           //pin d1
int fired = 0;            //Debounce and force people to take their finger off the button to fire again
int reading = 0;          //reading the button
int mode = 0;             //for future use
int rateLimit = 750;     //how often can you send a pew pew sound request
int fireDuration = 100;   //how long to activate the laser (in milliseconds)
unsigned long currentMillis = millis();
unsigned long rateLimitOver = 0; //When am I allowed to send another MQTT message requesting a pew
unsigned long canFireAgain  = 0; //When am I allowed to send another laser burst

int id = 59;              //serves as the final ip address and unique identifier
int subnet = 95;          //the second to last set of numbers in your ip addresses 192.168.SUBNET.xxx
int serverip = 148;       //what is the last part of the ip address of your nodered/mqtt server 192.168.SUBNET.SERVERIP
const char* ssid = "YOUR_SSID_HERE";
const char* password = "YOUR_WIFI_PASSWORD_HERE";

//Networking / MQTT Variables that usually don't need to be changed
WiFiClient ethClient;
PubSubClient client(ethClient);
char *cstring;
IPAddress arduino_ip ( 192,  168,   subnet,  id);
IPAddress dns_ip     (  8,   8,   8,   8);
IPAddress gateway_ip ( 192,  168,   subnet,   1);
IPAddress subnet_mask(255, 255, 255,   0);
IPAddress server(192, 168, subnet, serverip);
int request = 0;

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(DEVICE)) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("shooting/lasercheck", DEVICE);
      // ... and resubscribe
      // client.subscribe("relays/#");


    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 20 seconds");
      // Wait 20 seconds before retrying
      delay(20000);
    }
  }
}



void setup() {
  Serial.begin(57600);
  pinMode(laser, OUTPUT);
  pinMode(button, INPUT_PULLUP);
  client.setServer(server, 1883);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.config(arduino_ip, gateway_ip, subnet_mask);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("Booted");
  delay(1500);
}

void loop()
{
  if (!client.connected()) {
    reconnect();
      }
  client.loop();

  currentMillis = millis();  //get the current time of this loop


  reading = digitalRead(button);      //find the state of the button
  if(currentMillis >= canFireAgain){  //check and see if you should turn the laser off and allow refire
  digitalWrite(laser, LOW);
  if(fired == 0){                     //Only if the trigger has been released
      if(reading == LOW){
          Serial.println("firing");
          digitalWrite(laser, HIGH);

          canFireAgain = currentMillis + fireDuration;
          if(currentMillis > rateLimitOver){
            client.publish("shooting/pew", "1");
            rateLimitOver = currentMillis + rateLimit;
          }
          fired = 1;
      }
    }
  }


  if(fired == 1){
    reading = digitalRead(button);
    if(reading == HIGH){
      Serial.println("resetting");
      fired = 0;
    }
  }
}

Node Red Scoreboard

You will need to have Node-Red and Mosquitto installed and running on a PC or Raspberry Pi. They're both simple to install and run.

To import this code below, go to Node Red, go to the hamburger menu, import, clipboard and paste the code below. You will need to update the MQTT nodes with your own IP address for your MQTT server.

Node Red Code

[{"id":"38ca78d7.f31b08","type":"inject","z":"3627a10e.7a9c8e","name":"","topic":"","payload":"1","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":270,"y":820,"wires":[["f89d093d.090b08","5d1d3556.07aa1c","aef0b95e.ee0df8"]]},{"id":"da250fb0.3b4dc","type":"mqtt out","z":"3627a10e.7a9c8e","name":"","topic":"shooting/whodere","qos":"0","retain":"","broker":"e36485ee.59f3d8","x":670,"y":360,"wires":[]},{"id":"1b22e5f4.80a4ba","type":"mqtt in","z":"3627a10e.7a9c8e","name":"Units Check In","topic":"shooting/response","qos":"0","datatype":"auto","broker":"e36485ee.59f3d8","x":1580,"y":1200,"wires":[["cc2484df.25f8e8"]]},{"id":"72213d66.db4e04","type":"inject","z":"3627a10e.7a9c8e","name":"Activate #65","topic":"","payload":"4000","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":1670,"y":1260,"wires":[["949f9dfb.a4444"]]},{"id":"949f9dfb.a4444","type":"mqtt out","z":"3627a10e.7a9c8e","name":"","topic":"shooting/activate65","qos":"0","retain":"","broker":"e36485ee.59f3d8","x":1970,"y":1260,"wires":[]},{"id":"44c9fc6.e04ba04","type":"mqtt in","z":"3627a10e.7a9c8e","name":"","topic":"shooting/hit/#","qos":"0","broker":"e36485ee.59f3d8","x":1510,"y":320,"wires":[["afa8820.06b408","625171e5.a225f","76747054.d0178","a3aef5a4.0b2b88"]]},{"id":"625171e5.a225f","type":"debug","z":"3627a10e.7a9c8e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":2290,"y":300,"wires":[]},{"id":"f89d093d.090b08","type":"function","z":"3627a10e.7a9c8e","name":"Game Setup","func":"var startIp = 60;\nvar stopIp = 66;\nvar gameData  = {};   //Create a place to store your game data\nfor (var i = startIp; i <= stopIp; i++) {\n  var initial = [0,'click,mp3','boom.mp3'];\n    gameData[i] = initial;\n}\n\nglobal.set('startIp',startIp);   //What is the Lowest IP you have ever assigned for the game\nglobal.set('stopIp',stopIp);   //What is the highest IP you have ever assigned for the game\nglobal.set('minTime',1500); //The minimum amount of time to activate a target\nglobal.set('maxTime',7500); //These ar in ms\nglobal.set('gameTime',60) ;  //In seconds\nglobal.set('score',0);    //how many targets have been hit\nglobal.set('shown',0);    // how many targets have been shown\nglobal.set('lastTarget',0); //for internal use\n\nmsg.payload = 1; //this is what the arduinos look for to check in\nglobal.set('gameData',gameData);\n\nreturn msg;\n","outputs":1,"noerr":0,"x":370,"y":200,"wires":[["12715b12.686bc5","da250fb0.3b4dc","795d9d95.ae48f4","5c2cd856.9729a8"]]},{"id":"12715b12.686bc5","type":"debug","z":"3627a10e.7a9c8e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":890,"y":300,"wires":[]},{"id":"c688128c.3dadc","type":"function","z":"3627a10e.7a9c8e","name":"Get Saved Data","func":"var gameData  = global.get('gameData');\nmsg.payload = gameData;\nglobal.set('gameData',gameData);\nreturn msg;\n","outputs":1,"noerr":0,"x":1800,"y":1320,"wires":[["7e725332.62f22c"]]},{"id":"7e725332.62f22c","type":"debug","z":"3627a10e.7a9c8e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1970,"y":1320,"wires":[]},{"id":"dd2f88cb.cdfa98","type":"inject","z":"3627a10e.7a9c8e","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":1620,"y":1320,"wires":[["c688128c.3dadc"]]},{"id":"cc2484df.25f8e8","type":"function","z":"3627a10e.7a9c8e","name":"Get Saved Data","func":"var incoming = msg.payload.split(\"|\");\nvar gameData  = global.get('gameData');\nvar arduino = parseInt(incoming[0]);\n\ngameData[arduino] = [1,incoming[1],incoming[2]];\nglobal.set('gameData',gameData);\nreturn msg;\n","outputs":1,"noerr":0,"x":1780,"y":1200,"wires":[["a3b0a765.d396d8"]]},{"id":"a3b0a765.d396d8","type":"debug","z":"3627a10e.7a9c8e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1970,"y":1200,"wires":[]},{"id":"a4d0fb96.e50e98","type":"ui_text","z":"3627a10e.7a9c8e","group":"d9d089df.9ddee8","order":1,"width":"8","height":"3","name":"Current Time","label":"Timer","format":"  <font size=\"+75\">{{msg.payload}}","layout":"col-center","x":1370,"y":820,"wires":[]},{"id":"537fd6a8.eb93a8","type":"switch","z":"3627a10e.7a9c8e","name":"Timer Switch Statement","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"stop","vt":"str"},{"t":"else"}],"checkall":"true","outputs":2,"x":868.9999313354492,"y":734.2000036239624,"wires":[["4ea1f005.a0f71"],["28b88e2d.c45b22"]]},{"id":"5d1d3556.07aa1c","type":"function","z":"3627a10e.7a9c8e","name":"Reset Function","func":"msg.reset = true;\nvar stop = global.set(\"stop\", 0);\n\nreturn msg;","outputs":1,"noerr":0,"x":653.9999160766602,"y":882.6000204086304,"wires":[["22b50424.73b71c"]]},{"id":"6e631ec0.95911","type":"inject","z":"3627a10e.7a9c8e","name":"Reset","topic":"","payload":"reset","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":"","x":130,"y":920,"wires":[["5d1d3556.07aa1c","aef0b95e.ee0df8","5c2cd856.9729a8"]]},{"id":"22b50424.73b71c","type":"function","z":"3627a10e.7a9c8e","name":"Reset Final","func":"var newMsg = { payload: msg.payload.length };\n\nmath = 0;\n\nnewMsg.payload = math;\nreturn newMsg;","outputs":1,"noerr":0,"x":882.6998977661133,"y":881.3999586105347,"wires":[["a4d0fb96.e50e98"]]},{"id":"4ea1f005.a0f71","type":"function","z":"3627a10e.7a9c8e","name":"Major Math Going on Here","func":"var newMsg = { payload: msg.payload.length };\nvar time = global.get(\"finalTime\");  \nvar major = global.get(\"majors\");\nvar minor = global.get(\"minors\");\nvar stop = global.get(\"stop\");\nvar time = time * 1; //force to number\nvar major = 30 * major;\nvar minor = 10 * minor;\nvar math = time + major + minor;\nvar  h = 0;\nvar  m = 0;\nvar  s = 0;\nvar  t = math;\n  \ns = t % 60;\nt = (t - s)/60;\nm = t % 60;\nt = (t - m)/60;\nh = t;\nmath = h+\"h \"+m+\"m \"+s+\"s\";\n\nnewMsg.payload = math;\nreturn newMsg;\n","outputs":1,"noerr":0,"x":1300.9999923706055,"y":719.0000286102295,"wires":[[]]},{"id":"ee4c4904.c2ca28","type":"inject","z":"3627a10e.7a9c8e","name":"StartTimer","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":"","x":120,"y":120,"wires":[["f89d093d.090b08","63088a95.a6d624"]]},{"id":"ee79749d.3ac228","type":"looptimer","z":"3627a10e.7a9c8e","duration":".5","units":"Second","maxloops":"99999999","maxtimeout":"1","maxtimeoutunits":"Hour","name":"","x":480,"y":540,"wires":[["f3b6abe7.137608"],[]]},{"id":"f3b6abe7.137608","type":"counter","z":"3627a10e.7a9c8e","name":"","init":"0","step":".5","lower":"","upper":"","mode":"increment","outputs":"1","x":680,"y":520,"wires":[["91806719.0ded08"]]},{"id":"91806719.0ded08","type":"function","z":"3627a10e.7a9c8e","name":"getCount","func":"var newMsg = {payload:msg.count};\nvar timer = newMsg.payload;\nvar time = global.set(\"finalTime\", timer); \nreturn newMsg;","outputs":1,"noerr":0,"x":840,"y":520,"wires":[["537fd6a8.eb93a8","3416d337.b9863c"]]},{"id":"903ea1e4.8079a","type":"inject","z":"3627a10e.7a9c8e","name":"","topic":"","payload":"stop","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":"","x":90,"y":700,"wires":[["ee79749d.3ac228","87d8c403.9987e8"]]},{"id":"aef0b95e.ee0df8","type":"function","z":"3627a10e.7a9c8e","name":"Reset Time","func":"msg.reset = true;\nreturn msg;","outputs":1,"noerr":0,"x":651.9999351501465,"y":839.5999803543091,"wires":[["f3b6abe7.137608"]]},{"id":"28b88e2d.c45b22","type":"function","z":"3627a10e.7a9c8e","name":"forceTWO","func":"var time = msg.payload;\nmsg.payload = (Math.floor(time*100)/100).toFixed(2);\nreturn msg;","outputs":1,"noerr":0,"x":1084.0999946594238,"y":796.2000017166138,"wires":[["a4d0fb96.e50e98"]]},{"id":"87d8c403.9987e8","type":"function","z":"3627a10e.7a9c8e","name":"stop","func":"var stop = global.set(\"stop\", 1);\nreturn msg;","outputs":1,"noerr":0,"x":445.09992599487305,"y":740.1999998092651,"wires":[["537fd6a8.eb93a8"]]},{"id":"3416d337.b9863c","type":"function","z":"3627a10e.7a9c8e","name":"","func":"var endTime = global.get('gameTime') ;  //In seconds\n\nif(msg.payload >= endTime){\n    global.set('stop',1);\n    msg.payload = \"stop\";\n    return msg;\n}\n","outputs":1,"noerr":0,"x":990,"y":520,"wires":[["ee79749d.3ac228"]]},{"id":"795d9d95.ae48f4","type":"delay","z":"3627a10e.7a9c8e","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":440,"y":360,"wires":[["ee79749d.3ac228","d969955b.7f5748","ba7384d7.b525a8"]]},{"id":"7c2fc063.4d213","type":"ui_text","z":"3627a10e.7a9c8e","group":"d9d089df.9ddee8","order":1,"width":"8","height":"3","name":"Targets Hit","label":"Targets Hit","format":"  <font size=\"+75\">{{msg.payload}}","layout":"col-center","x":2290,"y":380,"wires":[]},{"id":"afa8820.06b408","type":"function","z":"3627a10e.7a9c8e","name":"Set Score","func":"var stop = global.get('stop');\nif(stop === 0){ //don't look for input if the game has been stopped!\nvar score = global.get('score');    //how many targets have been hit\nscore = score + 1;\nglobal.set('score',score);\nmsg.payload = score;\nreturn msg;\n}","outputs":1,"noerr":0,"x":1780,"y":280,"wires":[["7c2fc063.4d213"]]},{"id":"5c2cd856.9729a8","type":"change","z":"3627a10e.7a9c8e","name":"Set Zeros","rules":[{"t":"set","p":"payload","pt":"msg","to":"0","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1240,"y":520,"wires":[["7c2fc063.4d213","e4397bc3.cb7ec8"]]},{"id":"95121187.f3c35","type":"function","z":"3627a10e.7a9c8e","name":"","func":"var stop = global.get('stop');\nif(stop === 0){ //don't look for input if the game has been stopped!\nvar shown = global.get('shown');    //how many targets have been hit\nshown = shown + 1;\nglobal.set('shown',shown);\nmsg.payload = shown;\nreturn msg;\n}","outputs":1,"noerr":0,"x":1810,"y":520,"wires":[["e4397bc3.cb7ec8"]]},{"id":"e4397bc3.cb7ec8","type":"ui_text","z":"3627a10e.7a9c8e","group":"d9d089df.9ddee8","order":1,"width":"8","height":"3","name":"Targets Shown","label":"Targets Shown","format":"  <font size=\"+75\">{{msg.payload}}","layout":"col-center","x":2000,"y":580,"wires":[]},{"id":"d969955b.7f5748","type":"function","z":"3627a10e.7a9c8e","name":"Show Another Target","func":"var stop = global.get('stop');\nif(stop === 0){\nvar gameData = global.get('gameData');\nvar startIp = global.get('startIp');\nvar stopIp = global.get('stopIp');\nvar lastTarget = global.get('lastTarget');\n\nvar avail = [];\nfor (var i = startIp; i <= stopIp; i++) {\n\tif(gameData[i][0] === 1 && i != lastTarget){\n\t\tavail.push(i);\n}\n}\n\nvar random = getRandomInt(0, avail.length-1);\nglobal.set('lastTarget',avail[random]);\nmsg.target = avail[random];\nmsg.topic = \"shooting/activate\"+avail[random];\nmsg.payload = getRandomInt(global.get('minTime'),global.get('maxTime'));\nreturn msg;\n}\n\nfunction getRandomInt(min, max) {\n    min = Math.ceil(min);\n    max = Math.floor(max);\n    return Math.floor(Math.random() * (max - min + 1)) + min;\n}","outputs":1,"noerr":0,"x":1660,"y":900,"wires":[["95121187.f3c35","96f663a9.98daf","aa11cceb.ef1c9"]]},{"id":"9b083f4d.16965","type":"mqtt in","z":"3627a10e.7a9c8e","name":"","topic":"shooting/miss/#","qos":"0","datatype":"auto","broker":"e36485ee.59f3d8","x":1300,"y":320,"wires":[["d969955b.7f5748"]]},{"id":"96f663a9.98daf","type":"mqtt out","z":"3627a10e.7a9c8e","name":"Turn on new target","topic":"","qos":"0","retain":"","broker":"e36485ee.59f3d8","x":1990,"y":640,"wires":[]},{"id":"63088a95.a6d624","type":"exec","z":"3627a10e.7a9c8e","command":"c:\\xampp\\htdocs\\mosquitto\\cmdmp3","addpay":false,"append":"c:\\xampp\\htdocs\\mosquitto\\sounds\\ready.mp3","useSpawn":"true","timer":"","oldrc":false,"name":"","x":810,"y":120,"wires":[[],[],[]]},{"id":"ba7384d7.b525a8","type":"exec","z":"3627a10e.7a9c8e","command":"c:\\xampp\\htdocs\\mosquitto\\cmdmp3","addpay":false,"append":"c:\\xampp\\htdocs\\mosquitto\\sounds\\go.mp3","useSpawn":"false","timer":"","oldrc":false,"name":"","x":810,"y":420,"wires":[[],[],[]]},{"id":"3427f3c8.fbe97c","type":"exec","z":"3627a10e.7a9c8e","command":"c:\\xampp\\htdocs\\mosquitto\\cmdmp3","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":2330,"y":460,"wires":[[],[],[]]},{"id":"76747054.d0178","type":"function","z":"3627a10e.7a9c8e","name":"Play hit sound","func":"var hit = parseInt(msg.payload);\nvar gameData = global.get('gameData');\nvar mp3 = gameData[hit][2];\nmsg.payload = 'c:\\\\xampp\\\\htdocs\\\\mosquitto\\\\sounds\\\\'+mp3;\nreturn msg;","outputs":1,"noerr":0,"x":1930,"y":360,"wires":[["625171e5.a225f","3427f3c8.fbe97c"]]},{"id":"aa11cceb.ef1c9","type":"function","z":"3627a10e.7a9c8e","name":"Play show sound","func":"var grab = msg.target;\nvar gameData = global.get('gameData');\nvar mp3 = gameData[grab][1];\nmsg.payload = 'c:\\\\xampp\\\\htdocs\\\\mosquitto\\\\sounds\\\\'+mp3;\nreturn msg;","outputs":1,"noerr":0,"x":1990,"y":900,"wires":[["5857cc82.f8f404"]]},{"id":"a3aef5a4.0b2b88","type":"delay","z":"3627a10e.7a9c8e","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"1","randomUnits":"seconds","drop":false,"x":1600,"y":660,"wires":[["d969955b.7f5748"]]},{"id":"5857cc82.f8f404","type":"exec","z":"3627a10e.7a9c8e","command":"c:\\xampp\\htdocs\\mosquitto\\cmdmp3","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":2370,"y":860,"wires":[[],[],[]]},{"id":"4550176c.537658","type":"mqtt in","z":"3627a10e.7a9c8e","name":"Units Check In","topic":"shooting/pew","qos":"0","datatype":"auto","broker":"e36485ee.59f3d8","x":300,"y":1040,"wires":[["6b5c9a1b.9ad894","9cfb23af.567d"]]},{"id":"6b5c9a1b.9ad894","type":"exec","z":"3627a10e.7a9c8e","command":"c:\\xampp\\htdocs\\mosquitto\\cmdmp3","addpay":false,"append":"c:\\xampp\\htdocs\\mosquitto\\sounds\\laser.mp3","useSpawn":"false","timer":"","oldrc":false,"name":"","x":670,"y":1040,"wires":[[],[],[]]},{"id":"9cfb23af.567d","type":"debug","z":"3627a10e.7a9c8e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":600,"y":1120,"wires":[]},{"id":"d5ec75c.fdb1488","type":"ui_button","z":"3627a10e.7a9c8e","name":"","group":"d9d089df.9ddee8","order":7,"width":0,"height":0,"passthru":false,"label":"Reset","tooltip":"","color":"","bgcolor":"","icon":"","payload":"reset","payloadType":"str","topic":"","x":110,"y":1020,"wires":[["5d1d3556.07aa1c","aef0b95e.ee0df8","5c2cd856.9729a8"]]},{"id":"e21be815.8f0ea8","type":"ui_button","z":"3627a10e.7a9c8e","name":"","group":"d9d089df.9ddee8","order":7,"width":0,"height":0,"passthru":false,"label":"Stop","tooltip":"","color":"","bgcolor":"","icon":"","payload":"stop","payloadType":"str","topic":"","x":70,"y":580,"wires":[["87d8c403.9987e8","ee79749d.3ac228"]]},{"id":"cc0f4c46.c819d","type":"ui_button","z":"3627a10e.7a9c8e","name":"","group":"d9d089df.9ddee8","order":7,"width":0,"height":0,"passthru":false,"label":"Start","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"date","topic":"","x":90,"y":40,"wires":[["f89d093d.090b08","63088a95.a6d624"]]},{"id":"e36485ee.59f3d8","type":"mqtt-broker","z":"","name":"","broker":"127.0.0.1","port":"1883","tls":"69ba6ca6.b34184","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"d9d089df.9ddee8","type":"ui_group","z":"","name":"Timer","tab":"3ae5cf15.e3feb","order":1,"disp":true,"width":"8","collapse":false},{"id":"69ba6ca6.b34184","type":"tls-config","z":"","name":"","cert":"","key":"","ca":"","certname":"","keyname":"","caname":"","servername":"","verifyservercert":false},{"id":"3ae5cf15.e3feb","type":"ui_tab","z":"","name":"Shooting Gallery","icon":"dashboard","disabled":false,"hidden":false}]

Node-Red Scoreboard

Node Red Scoreboard
Dan
Dan

Comments are closed.