Help with random numbers

I am trying to use an Arduino to open random locks at a random time, I have some code but it re uses the same pin output more than once and I don't want it to do this. It needs to use all of the outputs and only once. Thanks in advance.

void setup () { 
  for (int i = 0; i > 13; i++) {
    pinMode(i, OUTPUT) ; // setup all of the neccesary LED pins
  }
  pinMode(2, INPUT) ; // setup the button pin 
  Serial.begin(9600) ;  // setup the serial for debugging 
}
void LightSequence() {
  int randomNum = random(10, 14) ; // pick random light to turn on. 
  // the lowest variable is the lowest light that turns on, and the highest is
  // the one above the higheest light, so that can be adjusted for more or 
  // less lights 
  Serial.println(randomNum); // print the chosen light in the serial 
  digitalWrite(randomNum, HIGH) ; // turn on the randomly chosen light 
  int randomTime = random(500, 3000) ; // pick a random amount of time to wait, the lowest being the minimum and the highest being the maxium
  Serial.println(randomTime); // print the chosen time in the serial 
  delay(randomTime) ;  //wait the random time 
  digitalWrite(randomNum, LOW) ; // turn off the light 
}
void LightDisplay(){
  LightSequence() ; // trigger a random light, and wait for it to turn off
  LightSequence() ; // trigger a random light, and wait for it to turn off
  LightSequence() ; // trigger a random light, and wait for it to turn off
  LightSequence() ; // trigger a random light, and wait for it to turn off


  Serial.println("End"); //print end in the serial to show when the sequence has ended
}
void loop () {
  int button = digitalRead(2) ; //setup the button's value to be used
  if (button == 1) { //when the button is pressed
    LightDisplay() ; //trigger the light sequence
  }
}

Please follow the advice given in the link below when posting code, in particular the section entitled 'Posting code and common code problems'

Use code tags (the </> icon above the compose window) to make it easier to read and copy for examination

This loop does not do what you expect:

Perhaps you meant to write

  for (int i = 0; i < 13; i++) {

You should avoid setting or using the serial TX and RX pins.

You can use an array; each element represents a pin.

Once you have randomly selected an element, mark it in the array. Next time that you pick a random number, check if it's marked in the array or not.

I have not used arrays before, quite new to this but will have a look at some youtube videos. Many thanks

This should get you close. It basically picks one of the pins from the array and uses it. It then swaps the last element of the array into the used slot and reduces the count of pins left to choose from.

FYI.. if you don't put in the randomSeed() function, every time you run the code, you will get the exact same sequence of "random" numbers so it is good to seed it.

byte pins[] = { 3,4,5,6,7,8,9,10,11,12,13 };
int pinCount = sizeof(pins) / sizeof(pins[0]);

const byte pinButton = 2;

void setup () {
  for (int i = 0; i < pinCount; i++) {
    pinMode(pins[i], OUTPUT) ; // setup all of the neccesary LED pins
  }
  pinMode(pinButton, INPUT) ; // setup the button pin
  Serial.begin(9600) ;  // setup the serial for debugging
  randomSeed(analogRead(A0)); // so you don't get the same sequence every time you run
}


void LightSequence() {
  int randomNum = random(pinCount) ; // pick random light to turn on.
  int pin = pins[randomNum];
  pins[randomNum] = pins[pinCount-1];
  pinCount--;
  Serial.println(randomNum); // print the chosen light in the serial
  digitalWrite(pin, HIGH) ; // turn on the randomly chosen light
  int randomTime = random(500, 3000) ; // pick a random amount of time to wait, the lowest being the minimum and the highest being the maxium
  Serial.println(randomTime); // print the chosen time in the serial
  delay(randomTime) ;  //wait the random time
  digitalWrite(pin, LOW) ; // turn off the light
}


void LightDisplay() {
  LightSequence() ; // trigger a random light, and wait for it to turn off
  LightSequence() ; // trigger a random light, and wait for it to turn off
  LightSequence() ; // trigger a random light, and wait for it to turn off
  LightSequence() ; // trigger a random light, and wait for it to turn off
  Serial.println("End"); //print end in the serial to show when the sequence has ended
}

void loop () {
  int button = digitalRead(pinButton) ; //setup the button's value to be used
  if (button == HIGH) { //when the button is pressed
    LightDisplay() ; //trigger the light sequence
  }
}

NOTE: if you press the button multiple times, pinCount will go below zero! Fixing that is up to you :slight_smile:

Thats brilliant thanks for this. Would there be a way to add a second button to run the sequence with faster or slower times?

I actually may have this sorted, probably not the most efficient but it appears to work.

Got it all sorted, probably not the most efficient way but it works and I have even created a little countdown piezo and two buttons for different sequence times.

byte pins[] = {9,10,11,12, };
int pinCount = sizeof(pins) / sizeof(pins[0]);
int piezoPin = 8;

const byte pinButton = (2,4);

void(* resetFunc) (void) = 0;

void setup () {
  for (int i = 0; i < pinCount; i++) {
    pinMode(pins[i], OUTPUT) ; // setup all of the neccesary LED pins
  }
  pinMode(pinButton, INPUT) ; // setup the button pin
  Serial.begin(9600) ;  // setup the serial for debugging
  randomSeed(analogRead(A0)); // so you don't get the same sequence every time you run
}


void LightSequence() {
  int randomNum = random(pinCount) ; // pick random light to turn on.
  int pin = pins[randomNum];
  pins[randomNum] = pins[pinCount-1];
  pinCount--;
  Serial.println(randomNum); // print the chosen light in the serial
  digitalWrite(pin, HIGH) ; // turn on the randomly chosen light
  int randomTime = random(500, 3000) ; // pick a random amount of time to wait, the lowest being the minimum and the highest being the maxium
  Serial.println(randomTime); // print the chosen time in the serial
  delay(randomTime) ;  //wait the random time
  digitalWrite(pin, LOW) ; // turn off the light
}
void LightSequence2() {
  int randomNum = random(pinCount) ; // pick random light to turn on.
  int pin = pins[randomNum];
  pins[randomNum] = pins[pinCount-1];
  pinCount--;
  Serial.println(randomNum); // print the chosen light in the serial
  digitalWrite(pin, HIGH) ; // turn on the randomly chosen light
  int randomTime = random(50, 7500) ; // pick a random amount of time to wait, the lowest being the minimum and the highest being the maxium
  Serial.println(randomTime); // print the chosen time in the serial
  delay(randomTime) ;  //wait the random time
  digitalWrite(pin, LOW) ; // turn off the light
}

void LightDisplay() {
  LightSequence() ; // trigger a random light, and wait for it to turn off
  LightSequence() ; // trigger a random light, and wait for it to turn off
  LightSequence() ; // trigger a random light, and wait for it to turn off
  LightSequence() ; // trigger a random light, and wait for it to turn off
  Serial.println("End"); //print end in the serial to show when the sequence has ended
  
}

void LightDisplay2() {
  LightSequence2() ; // trigger a random light, and wait for it to turn off
  LightSequence2() ; // trigger a random light, and wait for it to turn off
  LightSequence2() ; // trigger a random light, and wait for it to turn off
  LightSequence2() ; // trigger a random light, and wait for it to turn off
  Serial.println("End"); //print end in the serial to show when the sequence has ended
}

void loop () {
  int button = digitalRead(2) ; //setup the button's value to be used
  if (button == HIGH) { //when the button is pressed
    delay (2000);
  tone(piezoPin, 100, 500);
  delay(1000);
  tone(piezoPin, 100, 500);
  delay(1000);
  tone(piezoPin, 100, 500);
  delay(1000);
  tone(piezoPin, 100, 500);
  delay(1000);
  tone(piezoPin, 1000, 2000);
  delay(3000);
    LightDisplay() ; //trigger the light sequence
    resetFunc();
  }
    int button4 = digitalRead(4) ; //setup the button's value to be used
  if (button4 == HIGH) { //when the button is pressed
    delay (2000);
  tone(piezoPin, 100, 500);
  delay(1000);
  tone(piezoPin, 100, 500);
  delay(1000);
  tone(piezoPin, 100, 500);
  delay(1000);
  tone(piezoPin, 100, 500);
  delay(1000);
  tone(piezoPin, 1000, 2000);
  delay(3000);
    LightDisplay2() ; //trigger the light sequence
    resetFunc();
  }
}

Glad you got your code working!

Something you may want to bear in mind for the future is a general programming principle called D.R.Y which stands for Don't Repeat Yourself. Basically, you take sections of code that are repeated or very similar and turn them into separate functions/classes that can be reused/repurposed.

With that in mind, here's how I might approach this personally:

PS: I've used my own button class KTS_Button.h (3.6 KB) that you're welcome to use, or you can add in your own button code/library.

PPS: If you don't care about printing the pin and LED 'on time' then I've added a slightly shorter lightSequence() function as a comment above in the code.

PPPS: I'm not a huge fan of using delays() as it blocks the rest of the code, but if this is the only code you're running and there isn't any other code to 'block', then it's all good. In your case it will mean that button presses will be ignored while the light/sound sequence is going but I suspect that's what you want anyway.

// Version 0.0

#define NUM_BUTTONS 2
#define NUM_LEDS 4
#define PIEZO_PIN 8

#include "KTS_Button.h"

KTS_Button buttons[NUM_BUTTONS] = { 7, 6 };

int LEDs[NUM_LEDS] = { 9, 10, 11, 12 }; 

// void lightSequence(unsigned long min, unsigned long max) {
//   int pin = random(0, NUM_LEDS);
//   digitalWrite(LEDs[pin], HIGH);
//   delay(random(min, max););
//   digitalWrite(LEDs[pin], LOW);
// }

void lightSequence(unsigned long min, unsigned long max) {
  int pin = random(0, NUM_LEDS);
  unsigned long randTime = random(min, max);

  digitalWrite(LEDs[pin], HIGH);
  delay(randTime);
  digitalWrite(LEDs[pin], LOW);

  Serial.print(F("Pin "));
  Serial.print(pin);
  Serial.print(F(" was on for "));
  Serial.print(randTime);
  Serial.print(F(" millis"));
  Serial.println();
}

void beep(unsigned int freq, unsigned long duration, unsigned long pause) {
  tone(PIEZO_PIN, freq, duration);
  delay(pause);
}

void toneSequence() {
  for (int i = 0; i < 3; i++) {
    beep(100, 500, 1000); // freq, duration, pause
  }
  beep(1000, 200, 3000); // Personally, I think this delay is too long.
}

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < NUM_LEDS; i++)
    pinMode(LEDs[i], OUTPUT);
  randomSeed(analogRead(A0));
}

void loop() {
  for (int i = 0; i < NUM_BUTTONS; i++) {
    if (buttons[i].read() == SINGLE_PRESS) {
      switch(i) {
        case 0:
          delay(2000); // Personally, I think this delay is not neeed.
          toneSequence();
          for (int j = 0; j < 4; j++)
            lightSequence(500, 3000); // Call lightSequence with the min and max delay times
        break;
        
        case 1:
          delay(2000); // Personally, I think this delay is not needed.
          toneSequence();
          for (int j = 0; j < 4; j++)
            lightSequence(50, 7500); // same lightSequence call with new delay min/maxes
        break;
      }
    }
  }
}

Hi,
Putting the randomseed at the top of your void loop(), instead of in the void setup(), may help the "randomness".

Tom.. :grinning: :+1: :coffee: :australia:

1 Like

I realised I forgot the 'Only let each LED flash once' malarky. Not a problem I've tried to solve before but I took this approach and it seems to work:

// Version 0.1

#define NUM_BUTTONS 2
#define NUM_LEDS 4
#define PIEZO_PIN 8

#include "KTS_Button.h"

KTS_Button buttons[NUM_BUTTONS] = { 7, 6 };

int LEDs[NUM_LEDS] = { 9, 10, 11, 12 };
int usedIndex[NUM_LEDS] = { -1, -1, -1, -1 };

void addUsedIndexes(int pinIndex) {
  for (int i = 0; i < NUM_LEDS; i++) {
    if (usedIndex[i] == -1) { 
      usedIndex[i] = pinIndex;
      return;
    }
  }   
}

void resetUsedIndexes() {
  for (int i = 0; i < NUM_LEDS; i++)
    usedIndex[i] = -1;
}

bool checkIndexUsed(int pinIndex) {
  for (int i = 0; i < NUM_LEDS; i++) {
    if (usedIndex[i] == pinIndex)
      return true;
  }
  return false;
}

int getUnusedIndex() {
  int pinIndex = random(0, NUM_LEDS);
  while(checkIndexUsed(pinIndex)) {
    pinIndex = random(0, NUM_LEDS);
  }
  addUsedIndexes(pinIndex);
  return pinIndex;
}

void lightSequence(unsigned long min, unsigned long max) {
  int randPinIndex = getUnusedIndex();
  unsigned long randTime = random(min, max);

  digitalWrite(LEDs[randPinIndex], HIGH);
  delay(randTime);
  digitalWrite(LEDs[randPinIndex], LOW);

  Serial.print(F("LED "));
  Serial.print(randPinIndex);
  Serial.print(F(" was on for "));
  Serial.print(randTime);
  Serial.print(F(" millis"));
  Serial.println();
}

void beep(unsigned int freq, unsigned long duration, unsigned long pause) {
  tone(PIEZO_PIN, freq, duration);
  delay(pause);
}

void toneSequence() {
  for (int i = 0; i < 3; i++) {
    beep(100, 500, 1000); // freq, duration, pause
  }
  beep(1000, 200, 1000);
}

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < NUM_LEDS; i++)
    pinMode(LEDs[i], OUTPUT);
}

void loop() {
  randomSeed(analogRead(A0));   
  for (int i = 0; i < NUM_BUTTONS; i++) {
    if (buttons[i].read() == SINGLE_PRESS) {
      switch(i) {
        case 0:
          toneSequence();
          for (int j = 0; j < 4; j++)
            lightSequence(500, 3000); // Call lightSequence with the min and max delay times
          resetUsedIndexes();
        break;
        
        case 1:
          toneSequence();
          for (int j = 0; j < 4; j++)
            lightSequence(50, 7500); // same lightSequence call with new delay min/maxes
          resetUsedIndexes();
        break;
      }
    }
  }
}

See it here: RandomLights.ino - Wokwi Arduino Simulator

You are a legend, thanks for the help

What is wrong with the Bounce2 library? It supports button debouncing, long/short press, etc.

That's like saying 'what's wrong with buying a pizza?' when someone's made their own.

I know what I´ve used to made my own pizza. :nerd_face:

True! Plus if you want to improve your Italian cuisine skills, or at the very least know how to make something that resembles a pizza then you'd probably have a hard time of it without actually making a pizza or two :smiley: