So, there seem to be an inordinate number of "how do I get random numbers on the Arduino that don't suck" posts. This is understandable, because as we all know, the RNG always starts with the same seed, and therefore always produces the same string of "random" numbers.
The stock advice of "seed the RNG with an unused analog pin" is grossly insufficient, because reads from said analog pins are not in any way random, and fall in a small range of values.
So, here's my method:
Take only the lowest bits (LSB) from two analogRead calls
Put them through a von Neumann extractor - if the bits are equal, they are discarded. If the bits differ, the first bit is used
Collect 32 bits, and assemble them into a 32 bit number
Repeat (1) - (3) several times, and hash the results together with a hash function.
Hash in the micros counter. This may seem silly, but since this function takes a different amount of time each time it's run, the microseconds counter will be different each time.
Use the result of the hash to seed the RNG
Code below:
/* initial seed - this is the first 32-bits
of the fractional part of pi - no particular
reason, just a "nothing-up-my-sleeve" number
to ensure that the seed doesn't initialize
to "0".
*/
uint32_t seed = 0x243F6A88;
uint32_t hash(uint32_t x){
// Fowler-Noll-Vo hash function
uint32_t c;
for (uint8_t i = 0; i < 4; i++) {
c = x>>(i*8) & 0xff;
seed ^= c;
seed *= 0x01000193;
}
}
uint32_t seed_rng(){
// loop 16 times
for(uint8_t i=0; i<16; i++){
/* von Neumann extractor -
* gets two bits from analogRead
* if they are equal, discard
* if they are different, uses the
* first bit. Loops until 32 bits are
* collected
*/
uint8_t bits = 0;
// use uint32, because shifting by more than
// the length of a variable will throw a
// warning at compile time
uint32_t a, b, n = 0;
while(bits < 32){
// get LSB of two analogReads, delaying
// 256 uS in between
a = analogRead(A0) & 0b1;
delayMicroseconds(256);
b = analogRead(A0) & 0b1;
if(a !=b){
// if bits are not equal, use bit "a"
n |= a << bits;
bits++;
}
}
// hashes number with existing seed
hash(n);
n = 0;
}
// hash in microseconds counter for some
// added entropy, because why not?
hash(micros());
// seed Arduino RNG
randomSeed(seed);
}
Instructions for Use:
Place the above code in the body of the project (before setup and loop)
Call seed_rng() from setup. It generally takes a second or two to run, depending on a variety of factors
Generate random numbers using the Arduino random() function as usual
Caveats - This is designed for game / novelty / non-crypto applications. I have not run this through any formal, statistical tests for randomness, so safety for applications requiring this is not guaranteed.
Apologies about my garbage code; I'm a Python programmer, not a C developer.
If anyone tries this out or has any questions / comments / compliments / feedback / complaints, or just feels like replying, please do.
Also, I'm thinking of packaging this together into a nice, clean library if anyone is interested.
I had a similar system running with a bunch of neon flicker-flame bulbs. (Running on a Linux box - the thought of video processing on an 8-bit micro fills me with terror lol)
I did some thinking about random myself, but then others told me it was no good.
The start of your idea is analogRead(A0), but that is not a good start. It could be stuck to 0 or 1023. A better start is using multiple analog pins that are not connected and are floating between 0 and 1023. However, if the hardware is not known, then there is no guarantee that the analog inputs change.
During manufacturing of the processor, the manufacturer might put numbers in the chip. For the ATmega32U4 there is even a guaranteed unique manufacturer number in it.
Also the DS18B20 temperature sensors have a guaranteed unique identifier (note: this goes for the genuine DS18B20, every DS18B20 on Ebay/AliExpress/Amazon is counterfeit).
If a project is build multiple times, then they can be used to be sure that each build has a different random sequence.
Some processors have an internal temperature or hall sensor. The new RP2350 has a hardware random number generator.
Conclusion: There are many ways for random and it depends on the processor and the circuit what the best random is.
It seems strange to me that you extol the virtues of your marvelous method of seeding a random number generator - and then go on to say that you haven't tested it statistically for randomness.
Instead of using micros() it would be better to use last byte of micros() since it is "more random" unlike the full value.
if you have an RTC (with a battery) on your board then it is better to derive your "seed" from an RTC chip: multiply seconds, hours, etc, then take middle two bytes of your 32bit result.
if you have a "boot number counter" or something similar - hash it in as well
If you use your rng in sensetive applications (e.g. password/key generation) then stick to a hardware source of random bits. Hot resistor or p-n junction are good source of random noise.
Thank you for catching me on that - this is intended for AVR-based platforms. It's been tested on the 328p and 32u4.
It's unlikely that you'd see a run of absolutely identical values from analogRead, though I neglected to install a sanity check / timeout if it does occur; said condition would cause an infinite loop.
One analog input with a LDR is enough for a some random.
However, when all analog inputs are connected to GND then they are always zero.
For example when there is a resistor from the analog input to GND and with connectors to sensors, and the sensors are not connected yet.
And snide commentary like this is why many folks don't bother to post on forums (here or elsewhere), to share ideas.
This is simply something that I use in my personal projects; not touting it to be revolutionary, marvelous, or even good. Simply a suggestion for a better (and relatively straightforward) method than the single analogRead that the Arduino documentation suggests.
I stated that I did not run formal, statistical randomness tests (DieHard and the like). This does not mean it's completely untested, or hasn't been evaluated for randomness in any way.