Search and destroy bomb prop for paintball

Hello!

First, I apologize for asking about something that I've already seen asked. I have been unsuccessful in following some of the other guides available for this project.

I am trying to craft a prop for scenario paintball games. Most people know the Call of Duty game mode "Search and Destroy" where one team plants a bomb while the other defends or defuses the bomb and that's the prop I hope to craft.

The guides I have found cover the objective pretty well but don't include sufficient granular steps for me to follow or they don't explain how the prop actually works when used, limiting my ability to understand the build. All of them include features I don't want and my criteria may be easier to deliver and might not require an Arduino board.

Since all of the guides I reviewed included an Arduino board, I figured I'd come here and ask the professionals, first.

I have 15 years experience in IT, currently in cloud administration. I know enough about coding languages to read and vaguely understand many languages but I am far from a programmer.

The goal is straight forward:

  • Push and hold button A for 15 seconds to arm
  • 60 second timer (does not need to be visualized on LCD but would be nice)
  • Audible tone signalling device is armed
  • Push and hold button B for 15 seconds to disarm
  • Audible tone if timer reaches 0

If either button is released before the 15 seconds have passed, state does not change. The 60 second timer can stop while disarming but should not reset if the 15 second disarm time hasn't been reached. Optimally it would continue counting down through the 15 second disarm time, potentially reaching 0 before being successfully disarmed, resulting in a mission failure mid disarm attempt.

In any case, if the device is not disarmed within 60 seconds an audible tone should be activated and/or lights or other signs the time has expired.

Resetting the device with a simple on/off switch is sufficient, whereas the other guides I reviewed included game mode options and custom timer settings before the device was ready for play.

First question: Is an Arduino board necessary or is there a more analog/simplistic solution?

Second question: If Arduino is required, can anyone walk me through this like a novice? I tend to pick things up quick but I have no idea where to start reading for something like this.

Parts: (based on what I think is needed)

Logic board of some kind
Power/battery
Speaker
Two buttons
LEDs
LCD optional

All assistance is appreciated. This is a project for my 18 year old daughter who recently graduated high school and community college. She is interested in a programming career and this would show another side to programming outside of applications/video games.

Thanks!

Hello thehomelesstomato

Welcome to the worldbest Arduino forum ever.

This is a nice project to get started.

Keep it simple and stupid firstly.
Follow the example code that comes with the library or
run some tutorials for the hardware selected.
If you are happy with the results of the tutorials you can merge these to your project.

Have a nice day and enjoy coding in C++.

start painting a diagram of a finite state machine. If you have a diagram - "anybody" with a decent Arduino knowledge can program it.

just an example

1 Like

A quick straw poll suggests not.

1 Like

Word.

Yes.

Look at the "blink without delay" sketch to see the paradigmatic program for marking something happen once every so often.

The IPO model

The IPO model can be used here with those two concepts.

  • every time through the loop, read all the switches and check any timers then

  • with that info, and depends on what state you are in, maybe transition to a new state and finally

  • update all the displays accordianly.

I'd think about using a few digits of seven segment LED display, many TV/Movie bombs use that, mebbe this Search and Destroy is LCD orientated.

You could loop 10 times a second and have the (obligatory?) very precise timing render on three digits with 0.1 seconds ticking off.

Fun.

a7

1 Like

Get yourself one of the generic starter kits with buttons, sensors, etc., and follow a few starter projects and you should be just fine.

Here's a modification of the built-in example for state change detection to measure the time a button has been pressed. I think the methods in here go a long way towards handling detecting the "Push and hold button X" in your project.

image https://wokwi.com/projects/365724367327685633

/*
  State change detection (edge detection)

  Wokwi Sim: https://wokwi.com/projects/365724367327685633

  Modified from https://www.arduino.cc/en/Tutorial/BuiltInExamples/StateChangeDetection
    to use INPUT_PULLUP
    to measure and report time-in-state
    for https://forum.arduino.cc/t/search-and-destroy-bomb-prop-for-paintball/1130728
    
  Often, you don't need to know the state of a digital input all the time, but
  you just need to know when the input changes from one state to another.
  For example, you want to know when a button goes from OFF to ON. This is called
  state change detection, or edge detection.

  This example shows how to detect when a button or button changes from off to on
  and on to off.

  The circuit:
  - pushbutton attached to pin 2 from GND
  - LED attached from pin 13 to ground through 220 ohm resistor (or use the
    built-in LED on most Arduino boards)

  created  27 Sep 2005
  modified 30 Aug 2011
  by Tom Igoe

  This example code is in the public domain.

  https://www.arduino.cc/en/Tutorial/BuiltInExamples/StateChangeDetection
*/

// this constant won't change:
const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
unsigned long lastChangeMs = 0; // time of last state change

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT_PULLUP);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      lastChangeMs = millis();
      Serial.println("on");
      Serial.print("number of button pushes: ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;


  // turns on the LED every four button pushes by checking the modulo of the
  // button push counter. the modulo function gives you the remainder of the
  // division of two numbers:
  if (buttonPushCounter % 4 == 0) {
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }

  report();
}


void report(){
  unsigned long interval = 250;
  static unsigned long last = 0;
  unsigned long now = millis();
  if(now - last >= interval){
    last = now;
    Serial.print("state:");
    Serial.print(buttonState == HIGH? "HIGH":"LOW" );
    Serial.print(" time:");
    Serial.print( now - lastChangeMs );
    Serial.println();
  }
}

You could modify the last bit to do things based on inputs and state variables and time with this completely untested code:

  if (buttonState == LOW && millis() - lastChangeMs > 15000UL && ! armed) {
    armed = true;
    countdownStartMs = millis();
    digitalWrite(ledPin, HIGH);
    
  } else {
    ;
  }

...After some testing, here's some code with a cheap state machine built off of that same one-button state detection code handling an idle, armed and boom state. It would drop into the Wokwi simulation linked above in #5

image (click for live graphviz source)

/*
  State change detection (edge detection)

  Wokwi Sim: https://wokwi.com/projects/365724367327685633

  Modified from https://www.arduino.cc/en/Tutorial/BuiltInExamples/StateChangeDetection
    to use INPUT_PULLUP
    to measure and report time-in-state
    for https://forum.arduino.cc/t/search-and-destroy-bomb-prop-for-paintball/1130728

  Often, you don't need to know the state of a digital input all the time, but
  you just need to know when the input changes from one state to another.
  For example, you want to know when a button goes from OFF to ON. This is called
  state change detection, or edge detection.

  This example shows how to detect when a button or button changes from off to on
  and on to off.

  The circuit:
  - pushbutton attached to pin 2 from GND
  - LED attached from pin 13 to ground through 220 ohm resistor (or use the
    built-in LED on most Arduino boards)

  created  27 Sep 2005
  modified 30 Aug 2011
  by Tom Igoe

  This example code is in the public domain.

  https://www.arduino.cc/en/Tutorial/BuiltInExamples/StateChangeDetection
*/

// this constant won't change:
const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
unsigned long lastChangeMs = 0; // time of last state change
unsigned long lastLowMs = 0; // time of last state change

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT_PULLUP);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


unsigned long countdownStartMs = 0;
int armed = false;
unsigned long startOfStateMs = 0;




void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);
  unsigned long now = millis();

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    lastChangeMs = now;
    if (buttonState == LOW) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      lastLowMs = now;
      Serial.println("on");
      Serial.print("number of button pushes: ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;
 
  switch (armed) { // transition based on arm-state and button
    case 0: // idle
      if (buttonState == LOW && now - lastLowMs > 15000UL) {
        armed = 1;
        countdownStartMs = now;
        changeArming(1);
        digitalWrite(ledPin, HIGH);
      }
      break;
    case 1: // armed and counting
      if (now - startOfStateMs > 60000UL) { // boom
        Serial.print("boom");
        changeArming(5);
      }
      if (buttonState == LOW && now - lastLowMs > 15000UL) { // disarmed
        changeArming(0);
        digitalWrite(ledPin, LOW);
      }
      break;
    case 5: // boom
      Serial.print("B");
      if (buttonState == LOW && now - lastLowMs > 15000UL) { // disarmed
        changeArming(0);
        digitalWrite(ledPin, LOW);
      }
  }
  report();
}

void changeArming(int newState) {
  unsigned long now = millis();
  armed = newState;
  startOfStateMs = now; // record transition time
  lastChangeMs = now; // reset button time
  lastLowMs = now;
}

void report() {
  unsigned long interval = 250;
  static unsigned long last = 0;
  unsigned long now = millis();
  if (now - last >= interval) {
    last = now;
    Serial.print("state:");
    Serial.print(buttonState == HIGH ? "HIGH" : "LOW" );
    Serial.print(" time:");
    Serial.print( now - lastChangeMs );
    Serial.print(" armed:");
    Serial.print(armed);
    if (armed) {
      Serial.print(" armedTime:");
      Serial.print(now - countdownStartMs);
    }
    Serial.println();
  }
}

it uses just the one button and LED from the state change detection example for arming, disarming, and resetting.

To extend it to the OP's requirements, one ought to expand the built-in state-change button detection code to more (two) buttons and flesh out the LCD hardware and audio hardware with their own handler functions for reporting (perhaps add a reportLCD(){...} and maintenance handleAudio(){...}.

For reading about fancying it up and extending it further, I'd suggest this:

ETA:

1 Like

Hello! And thank you all for your replies.

While I understand the replies conceptually I don't think I have sufficient knowledge, or time, to build this device from the information in the replies.

I have enlisted the help of a professional colleague who just happens to know Arduino.

I will reply back with updates as the project progresses.

You could do that.

It would be interesting to see what "a professional colleague who just happens to know Arduino" comes up with, and of course the final physical device concept. Good luck.

Here's something similar but different.

By running the guts of the loop at 10 Hz, we can eliminate any need for switch debouncing and state change detection, at least the explicit implementation of those.

It all goes with the state. In the name of overkill I went full IPO on it, to the point of duplicating the switch/case structure to do the outputs. Try it here:

Wokwi_badge Search and Destroy BOOM!

// https://forum.arduino.cc/t/search-and-destroy-bomb-prop-for-paintball/1130728
// https://wokwi.com/projects/365754412965833729

# define disarmButton   2
# define armButton      3

# define disarmedLED    5
# define armedLED       6

# define boomLED        7

void setup() {
  Serial.begin(115200);
  Serial.println("Hello Search and Destroy World!\n");

  pinMode(armButton, INPUT_PULLUP);
  pinMode(disarmButton, INPUT_PULLUP);

  pinMode(armedLED, OUTPUT);
  pinMode(disarmedLED, OUTPUT);
}

enum {DISARMED, ARMED, BOOM};

unsigned char state = DISARMED;

# define RATE           100     // every 100 milliseconds

// life too short timing constants. adjust to taste (and tolerance)
# define TOARM          50      // 50 ticks is 5 seconds
# define TODISARM       50      // 5 seconds
# define TOBOOM         200     // 10 seconds

unsigned char armCount;
unsigned char disarmCount;
unsigned char boomCount;

unsigned long now;

void loop() {
  now = millis();   // time for all machines

  handleDevice();
  report();
}

void handleDevice()
{
// RATE mechanism
  static unsigned long lastTime;
  if (now - lastTime < RATE) return;    // not time yet to do any of the rest of this
  lastTime = now;

// INPUT
  unsigned char armIt = !digitalRead(armButton);
  unsigned char disarmIt = !digitalRead(disarmButton);

// PROCESS
  switch (state) {
  case DISARMED :
    disarmCount = 0;    // nephew presses all the button at once - who does that IRL?
    if (armIt) armCount++;
    else armCount = 0;

    if (armCount == TOARM) state = ARMED;
    break;

  case ARMED :
    armCount = 0;    // nephews, who needs them?
    if (disarmIt) disarmCount++;
    else disarmCount = 0;

    boomCount++;

    if (boomCount == TOBOOM) state = BOOM;
    if (disarmCount == TODISARM) {
      state = DISARMED;
      boomCount = 0;
    }

    break;

  case BOOM :
// boom is end of the world. no further state transitions
    break;
  }

// OUTPUT
  switch (state) {
  case DISARMED :
    digitalWrite(armedLED, LOW);
    digitalWrite(disarmedLED, HIGH);
    break;

  case ARMED :
    digitalWrite(armedLED, HIGH);
    digitalWrite(disarmedLED, LOW);
    break;

  case BOOM :
    digitalWrite(armedLED, LOW);
    digitalWrite(disarmedLED, LOW);

    digitalWrite(boomLED, HIGH);
    break;
  }
}

void report()
{
  static unsigned long lastTime;
  unsigned long now = millis();

  if (now - lastTime < 777) return;    // not time yet to do any of the rest of this
  lastTime = now;
 
  Serial.print("arm count "); Serial.println(armCount);
  Serial.print("disarm count "); Serial.println(disarmCount);
  Serial.print("boom count "); Serial.println(boomCount);
  Serial.println();
}

The state is actually a combination of the major state variable and the current contents of the relevant counters. So a bit of a nightmare.

a7

1 Like

That's a much cleaner implementation than my hack on the built-in StateChangeDetection example. The Input-Processing-Output is much clearer, and the state machine is clean. I like the separate switch-cases for the Processing and Output chunks. I like the clean use of functions, and how the switch/case structure makes it easy to figure out where one would put added features and functions. I always like a nice report() function in the loop().

If I would change anything I'd move the RATE/TOARM/TODISARM/TOBOOM user tick settings up higher, out of the middle of the global/state variables, and maybe do the seconds->count threshold math with the compiler. And add a wokwi-text block to the diagram.json file.

A fresh start design like yours is better than hacks tacked onto crufty examples like mine. I wish the built-in examples were more modular for better extensibility, but perhaps for the one-task examples, the overhead involved in demonstrating Input/process/output and state machines is too confusing.

Thanks.

It is always somewhat dismaying to see that a simple concept, or a few of them, can turn into lotsa code. I was working up something "simple" but soon enough I knew what I had was of little use to anyone other than ppl for whom it would be no revelation.

One thing I try to do is use the most basic language features possible; this lets noobs focus on the algorithm without getting bogged down with what can be side issues for a good long time.

I agree with your style and other recommendations. I did start writing the stuff that would let the compiler do the maths, but it started to look a mess because, TBH, I'm just not that good at it, to my detriment. I still do too much by hand. And I can't seem to move beyond # define, and accept all claims and criticisms.

But for this one I must give some credit to the Umbrella Academy and water just a bit too cold.

For your amusement, the real sketch that turned into > 100 lines of code

The rest was pesky details. I won't live long enough to see the AI to which we might feed such a thing and have it do that coding, maybe some who are reading this will.

For now I have fun doing all that. Never mind what doesn't get done because. :wink:

And yeah, IPO. I must always shout out to @paulpaulson for the relentless promotion. I remember read-eval-print from somewhere, but I had not carried that idea so explicitly into the Arduino/real world stuff.

a7

1 Like

Did I hear my name?

Has anyone refer to the IPO model?

I would take a bag of chained and eventdriven timers and program the thing.

Have a nice day and enjoy coding in C++.

2 Likes

A bag of these?

  • ArmButton-while-not-armed eventdriven timer
  • Armed state eventdriven timer
  • DisarmButton-while-armed eventdriven timer

That sounds feasible.

Give them a try.

Hello again! Your continued replies are greatly appreciated.

I engaged my colleague since I can get hands on explanation of the elements he designs, hopefully with less user error on my part trying to dive in and understand what's being provided here.

It'll be awesome to see if he designs what's been mentioned here but I will absolutely provide updates and progress. I have to start fashioning the prop itself.

Do you have your parts picked out?

This example uses a Piezo for a speaker:

This example uses a parallel interface LCD:

but most folks like LCDs with the I2C backpack serial interface since they don't require as many IO pins:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.