Is there a way to create a randomseed() without sampling an floating pin or user input?

I want to generate random numbers in my sketch for a project with an Attiny85 but I've used up all of my pins and don't have one to sample for noise. The project also doesn't have any user inputs that I could use to generate a seed. I've spent a bit of time looking this up but the best I've found are topics about entropy that are frankly far over my head.

The easiest solution I've come up with is to simply use an Attiny84 instead but I already have a handful of 85s and using the smaller chip would save me space on the PCB.

Is there an easy solution for the, preferably for a novice programmer who is not smart with the maths?

Edit: Added code and picture of schematic. I'm testing the code on an Arduino Uno R3 right now before I try putting it on the Attiny85, that is why the attiny pins are commented out and Uno pins are used instead.

The project is a motion activated music box that uses an ultrasonic sensor to detect movement and a motor attached to a hand crank music box mechanism. In addition having the music box being triggered by motion I want to also have a "haunted" mode that plays music at random intervals. This code does include an attempt I made at copy/pasting some code in to generate random numbers in the form of "getRandom()" at the bottom but I do not understand exactly what it is doing and the random number pattern ended up being the same every time I restarted the sketch.
Schematic v1- Dr Lilys Motion Detector Music Box-01

#include <NewPing.h>

#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
//#define TRIGGER_PIN  3  // Attiny85 Arduino pin tied to trigger pin on the ultrasonic sensor.
//#define ECHO_PIN     4  // Attiny85 Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 500 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

// Pin assignments
const int led = 2;   // LED is attached to pin 2
const int motor = 5;    // Motor is attached to pin 1
const int hauntedSelect = 3;
const int hauntedInterval = 5; // How often the haunted function triggers in seconds
const int hauntedDuration = 10; // How long the haunted music plays when triggered
//const int led = 0;   //Attiny85 
//const int motor = 1;    //Attiny85
//const in hauntedSelect = 2; //Attiny85

bool object_detected = false;
bool max_range = false; 
int sensorSamples = 0;
unsigned long previousMillis = 0;
unsigned long m_w = 1;  //variable for random number generation
unsigned long m_z = 2;  //variable for random number generation

void setup() {
  pinMode(led, OUTPUT);
  pinMode(motor, OUTPUT);
  pinMode(hauntedSelect, INPUT);

//Take range samples to determine range to nearest object in front of sensor
  int i;
  for (i = 0; i < 10; i++) {
    digitalWrite(led, !digitalRead(led));   //toggles LED on and off while calibrating range
    sensorSamples = sensorSamples + sonar.ping_cm();
     delay(200);
  }
  digitalWrite(led, LOW);

//Find the average of all samples.
//10 is subracted to give a buffer to compensate for minor reading variations
  sensorSamples = (sensorSamples / 10) - 10; 
  
  if(sensorSamples <= 0){   //the sensor will read 0 if it does not detect a surface
    max_range = true;
  }
}

void loop() {
  delay(50);                     // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.

  if(max_range){
    if(sonar.ping_cm() > 10){
      playMusic(2);
    }
  }
  else if(sonar.ping_cm() < sensorSamples){
    playMusic(2);
  }

  if(digitalRead(hauntedSelect)){
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis > (hauntedInterval * 1000)){ // 600000 miliseconds is ten minutes
      int randNumber;
      randNumber = getRandom();
      if(randNumber % 3 == 0){
        playMusic(hauntedDuration);
      }
      previousMillis = currentMillis;
    }
  }
}

void playMusic(int x){  // turns on the music box motor for x seconds 
  digitalWrite(motor, HIGH);
  for (int i = 0; i < x; i++) {   //loop and toggle LED every 1 second
    digitalWrite(led, !digitalRead(led));
    delay(1000);
  }
  while(sonar.ping_cm() < sensorSamples){
    digitalWrite(led, !digitalRead(led));
    delay(1000);
  }
  digitalWrite(motor, LOW);
  digitalWrite(led, LOW);
}

unsigned long getRandom()
{
    m_z = 36969L * (m_z & 65535L) + (m_z >> 16);
    m_w = 18000L * (m_w & 65535L) + (m_w >> 16);
    return (m_z << 16) + m_w; 
}

Post your code. Post enough schematic so the purpose of all pins is made perfectly clear.

It may be possible to use a pin differently to how it will ultimately serve you in your sketch for a brief moment during setup().

a7

If there is unpredictable analog input you can seed from that.

Please explain why seeding random numbers is important for your application.

1 Like

If you're after randomness, what matters is NOT using the same seed each time. What you DO use is not that important, as long as it does not repeat. Store the seed in EEPROM, and update it each time setup() runs.

I'm making a novelty "haunted music box" and I would like the intervals between it playing music to be random and unpredictable.

Thank you. If I'm understanding you correctly that as long as I'm using a different number each time it could be as simple as using "1" the first time the program loads and "2" the second time, and so on. By storing it on EEPROM I could increment it every time the program is run then restore it in EEPROM for the next time the program starts. Is this the gist of the idea?

I've never used EEPROM before. After looking it up am I correct that it would store this number in a single byte and the number would only go to 255? If so would adding one to that loop it back around to 1 or would I need to do that in the code somewhere myself? 1

Seeding does not seem to be necessary for your application.

Seeding is usually used in setup() to start the random number generator at a different place in the very, very long fixed sequence. This can be important for games, so that a game starts at a new place when the power is first turned on.

1 Like

That or you could just use the code from post #5.

1 Like

Thank you, that does sound like an interesting solution. I've edited my original post with code and a schematic of what I intend for the Attiny85. However, right now the code is written to use pins on an Uno R3 while I'm writing and testing it out.

Thank you, I'm certain that is what I will end up using and have just tested the code and it seems to do exactly what I want. Is that what your code is doing in essence? Reading from to EEPROM and changing the number every time the code restarts?

I'm not a good or experienced coder. In fact this is the first time I've made my own function that passes a variable. Typically in the past I've just copy pasted chunks of other people's code and dinked around with it until I get it to do what I want. I'm wanting to learn more and trying to at least understand the concept behind the code I'm copying.

1 Like

I've added a schematic to my original post. I'm not certain but I don't think I have an analog input to sample as they are all tied up with a sensor and driving a motor. I'm not really sure how to tell if one of those could be used.

Exactly.

Ask questions.

1 Like

You ONLY use the seed ONCE, in setup(), when the program starts. Then increment the value stored in EEPROM, and that updated value will be used the NEXT time the program starts. Using a byte will give you 256 different pseudo-random sequences. Using an int will give you 65,536 different sequences.

1 Like

Given that you have a HC-SR04 distance sensor, you might use it for extra entropy.
You could check if the first read is "noisy enough" to be used as seed.

In combination with EEPROM solution, use the first read to increment the seed too.

(note you have to divide the return time of the echo in micros by 4 as that is the resolution of the timing).

2 Likes

...for an AVR processor running at 16 MHz.

Oh my, that is a good idea! When the program starts it is very unlikely that the distance it first reads will be the same as the last time it was turned on. This would probably make a good seed.
Thanks!

The newping library says it has support for the Attiny85 so I'm hoping it handles this automatically. I will be using it at 8 MHz and will have to change the timing of somethings but I'll cross that bridge when I come it (hopefully).

Thank you again your help. I've learned a lot from this project already and I'm looking forward to starting something else that is more complicated soon! I've got a ghostbusters prop project in the works and I bought an ESP32 board and those should give me lots to tinker with.

1 Like

Got it. This is all making much more sense now. Thanks again!