Random alarm in a specified period

Hi all,
I am completely new to Arduino programming & I have just bought a starter kit. I'd like to learn & try various projects, but my main reason for getting started is to try and create a simple random alarm, that might (or might not) sound during a specified period.

I am sure this forum is not here to ask for code to be written for us, but I'd be grateful for any help in getting started with this.

I've searched the net and can find nothing that comes close to what I am looking for.

Here's my problem:
I play a lot of race simulations, and while the AI cars often suffer mechanical retirements during a race, the player's car usually does not suffer any failure like this. In order to run a completely fair championship against AI competition, I want to create a timer that might sound during the race, which would force me to immediately stop the car, due to some fictitious engine or electrical failure. This would give me, the player, the same chance as the AI, of failing to finish a race.

Within the code, I need to be able to specify

  1. the length of the race and
  2. the probability of the alarm sounding.
    So, for example, I may like to run a 1 hour race with a 15% chance of the 'retirement alarm' sounding.

All I need is a simple alarm to activate, but as a bonus, an LCD display reading 'Stop the car!' could also be added.

If anyone has read all that, firstly thank you, & secondly, is there anywhere I might find some help in learning how to program such a random alarm?

Thank you for reading.

it's feasible with a simple Arduino and a buzzer indeed.

if you don't want to have everything hardcoded, how do you plan to enter the data? (ie what's the expected User Interface?)

Once during every race, run a code segment something like this:

bool stop = false;  //flag that car has failed
if (random(0, 101) < 15) stop=true;   //will be true 15% of the time
if (stop) Serial.print("machine failed");

The function random(), when used this way, will return a series of integers between 0 and 100, one per call. It will be the same sequence each time you run the program, unless you call the function randomSeed() to change the sequence.

See random() - Arduino Reference

You can create a random number between 0 and 100, and then an if-statement to do something if the value is less than 15.

Similarly you can set a random timer, etc. That might be as simple as a delay() or you might want to use a millis() timer (see the Blink Without Delay Example).

delay() and millis() both use milliseconds, so an hour is 60,000ms.

Note that program execution is paused during the delay() time. That might be OK if it's just waiting to (possibly) activate the alarm. In general, long delays are avoided because often we don't want the processor "doing nothing". i.e. We might want to be able to respond to a button-push, or we might want to be updating a display or something. In those cases a millis() timer is the way to go.

Hi,
thanks for the replies, it doesn't need to be anything fancy, I just want to be able to add the required race duration (and probability?) into the code and upload it to the Uno, to run in the background while racing.
So for example, if I am running a 1 hour race, I might be able to tell an alarm to go off any time during a 3 hour period, which would mean there was a 33% chance of it sounding during the race. That would be perfectly adequate, as long as I could specify that duration in the code.

I should clarify, it needs to be running 'live' during the race. i.e. a timer that might or might not kick in, and tell me to retire. I cannot simply generate a random number to see if I will have to retire, as that will defeat the object of even starting the race.

(I hope that makes sense).

So, I envisage a timer of some sort, running along silently, and then suddenly activating (if I am unlucky), indicating a fault in the car.

The best analogy I can think of is the 'ticking bomb' game, where players pass a bomb between them, it is constantly ticking but no one knows when it will actually go off. Of course, if I can finish the race before it actually goes off, then that is the ideal scenario, but I want the possibility of failure to be there, as it is in all real motor races.

You write the program so that it is guaranteed to kick in. As has already been explained.

The computer (including the built in timers) does nothing "randomly".

Try this example, and then experiment with using the randomSeed() function.

int race_number=0;

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
while(!Serial);

}

void loop() {
  // put your main code here, to run repeatedly:
race_number++;
Serial.print("Race number ");
Serial.println(race_number);

delay(5000);                    //for nail biting suspense...

if (random(0,101) < 15) Serial.println("Bummer! Mechanical failure");
else Serial.println("I won!");
}

Using the idea proposed by @jremington — try this, a bit more involved

#include <Encoder.h>  // https://www.pjrc.com/teensy/td_libs_Encoder.html
#include <easyRun.h>  // https://github.com/bricoleau/easyRun

const byte encoderDTPin = 2;     // encoder encoder DT
const byte encoderCLKPin = 3;    // encoder encoder CLK
Encoder encoder(encoderDTPin, encoderCLKPin);

const byte encoderSWPin = 4;     // encoder encoder SW
button encoderSwitch(encoderSWPin);

long duration = 10;
long probability = 10;
bool alarmWillSound;
unsigned long alarmTime;
unsigned long startTime;

bool encoderChanged(long minVal, long maxVal, bool reset = false, long startVal = 0) {
  static long oldValue = -1000;
  if (reset) {
    oldValue = startVal;
    encoder.write(startVal << 1);
    return false;
  }

  long newValue = encoder.read() >> 1;
  if (newValue < minVal) {
    encoder.write(minVal << 1);
    newValue = minVal;
  } else if (newValue > maxVal) {
    encoder.write(maxVal << 1);
    newValue = maxVal;
  }

  if (newValue != oldValue) {
    oldValue =  newValue;
    return true;
  }
  return false;
}

void getDuration() {
  encoderChanged(1, 600, true, duration);  // prime the rotary
  Serial.println(F("\nEnter duration in min"));
  while (!encoderSwitch.pressed()) {
    easyRun();
    if (encoderChanged(1, 600)) {
      duration = encoder.read() >> 1;
      Serial.print(F("duration : "));
      Serial.print(duration);
      Serial.println(F(" min"));
    }
  }
  Serial.print(F("\n==> Duration set to: "));
  Serial.print(duration);
  Serial.println(F(" min"));
  while (encoderSwitch.pressed()) easyRun();  // wait for button to be released
}


void getProbability() {
  encoderChanged(0, 100, true, probability);  // prime the rotary
  Serial.println(F("\nEnter probability %"));
  while (!encoderSwitch.pressed()) {
    easyRun();
    if (encoderChanged(0, 100)) {
      probability = encoder.read() >> 1;
      Serial.print(F("probability : "));
      Serial.print(probability);
      Serial.println(F(" %"));
    }
  }
  Serial.print(F("\n==> Probability set to: "));
  Serial.print(probability);
  Serial.println(F(" %"));
  while (encoderSwitch.pressed()) easyRun();  // wait for button to be released
}


void setup() {
  randomSeed(analogRead(A0));
  Serial.begin(115200); Serial.println();

  getDuration();
  getProbability();

  alarmWillSound = (random(0, 101) <= probability);
  alarmTime = random(0, 60000ul * duration);

  // ----------------------
  // remove this if you don't want to know
  // ----------------------
  if (alarmWillSound) {
    Serial.print(F("Alarm will sound in ")); Serial.print(alarmTime / 1000); Serial.println(F(" s."));
  } else {
    Serial.print(F("Alarm will not sound."));
  }
  // ----------------------

  Serial.println(F("\n====== Click to start ======"));
  while (!encoderSwitch.pressed()) easyRun();
  startTime += millis();
  Serial.println(F("Started"));
}

void loop() {
  static unsigned long lastTime = startTime;

  if (millis() - lastTime >= 1000ul) {
    lastTime += 1000ul;
    Serial.println((millis() - startTime) / 1000ul);
  }

  if (millis() - startTime >= 60000ul * duration) {
    Serial.println(F("Race ended"));
    while (true) yield();
  } else if (alarmWillSound && (millis() - startTime >= alarmTime)) {
    Serial.println(F("ALARM !!!!!"));
    alarmWillSound = false;
  }
}

You need a rotary encoder with a button connected on pin 2 (DT), 3(CLK) and 4 (the switch)

you need to install those 1 libraries
to handle the encoder ➜ Encoder Library, for Measuring Quadarature Encoded Position or Rotation Signals
to handle the button ➜ GitHub - bricoleau/easyRun: Doing sereval things at the same time becomes so easy

Open the serial monitor at 115200 bauds and follow instructions
after the race you need to reset the arduino to start afresh

(totally untested)

Thank you all for the replies, I will see what I can produce from this.
I'm very grateful for such helpful information & pointers.

I do realise that, thanks. I meant 'might or might not kick in during the race', hence my suggestion that my timer be running for 3 hours, so it might also kick in after the race has concluded.

If it does, you made a mistake in writing the program, and of course would correct it.

No, we're obviously at cross purposes here.
If I could intentionally create a timer sketch that runs for 3 hours, and includes an alarm that will definitely activate just once at any time during that 3 hours; and if I start my 1 hour race at the same time that I start the timer sketch; then I would know that there is a 33% chance of the alarm activating during my 1 hour race. If it does not activate in the first hour, then my race concludes successfully without 'mechanical failure', and it matters not what happens after the race is over, as I would have completed my race and, if the alarm had not sounded, then I could cancel the sketch at that point, job done.

(Obviously, changing the duration of the timer would be all that is needed to alter the odds of a failure during the race, so running it for 6 hours would give me a ~16.5% chance of failure).

If you look at the code I posted, you can select the duration and the probability

The code will get the alarm with that probability and will show ticking time for the duration and at the given moment - if an alarm needs to be triggered, it will write Alarm (once) and then continue ticking until the end of the race

Thank you, it's much appreciated, I'm working my way through it and slowly understanding it, I'll definitely give it a try.

We do the best we can, given such vague suggestions, and your limited understanding about how a computer works and what it can be programmed to do.

I used to have a small Android app which did exactly what I describe, but I somehow lost it, so was hoping to recreate it using Arduino. Or maybe the Arduino isn't capable of this?

I agree there is certainly a lack of understanding somewhere.

My understanding must indeed be limited if I've been wrong to think a pc/Arduino could be programmed to run a simple timer for 3 hours and sound an alarm just once during that time.

But not to worry, I am looking at J-M-L's helpful posts and learning from them.

I realise now that I could have asked a more straightforward question to achieve the outcome I need - how to create a countdown timer with a duration of between 0 and 9 hours, set 'randomly' by the program but unknown to me, with an alarm sounding when countdown reaches zero.

Would this be more simple to code?

Did you try my code?
I just got to try it and it seems to work fine

If you don’t have a rotary encoder with push button you could devise another input strategy.

What hardware do you have?