Random number generation on the ESP8266
Soooo random!
For an ESP8266 project that sends randomised tweets, I need to use a random number generator. In such a deterministic system such as a digital computer however, true randomness is hard (impossible) to come by, so we must settle for a pseudo-random number - that is a number that is part of a seemingly random sequence and is generated algorithmically from the previous number(s) in the sequence. It is good enough for our purposes. The simplest implementation of a pseudo-random number generator (PRNG) is a Linear Congruential Generator, i.e.
Xn+1 = (aXn + c) mod m
We don't need to guess at suitable values of a, c and m as common values have been tabulated for various implementations (Wikipedia). From the first entry of the Parameters in Common Use table, I chose a = 1664525, c = 1013904223, m = 2^16 (rather than 32). Therefore we can define a data structure to hold the previous value in (in this example I also have "tweet_id" as I want to save this but it is of course not required for the PRNG.
struct { uint8_t tweet_id; uint32_t seed; }random_data; uint32_t nextrandom() { random_data.seed = (random_data.seed*1664525 + 1013904223) % 65536; return random_data.seed; }
If we ran this code and called the nextrandom function in a loop, we would get a sequence of random numbers... but the SAME sequence of random numbers every time we rebooted the ESP8266! In my case, I need a DIFFERENT random number every time I start as I want to randomly choose a tweet (and not the same tweet as the previous time). So what do we do? One answer is to save the last random number (random_data.seed) into EEPROM, then read it back in when we boot up and so continue the sequence.
Briefly EEPROM access is easy in the Arduino GUI:
// Setup and read from EEPROM EEPROM.begin(sizeof(random_data)); // Set aside some memory EEPROM.get(0, random_data); // Gets data from address 0 uint8_t prev_id = random_data.tweet_id; while(random_data.tweet_id==prev_id) { random_data.tweet_id = nextrandom() % NUM_TWEETS; // 0,1,2... NUM_TWEETS-1 } // Do things . . . // Save to EEPROM EEPROM.put(0,random_data); // This just writes to an array EEPROM.commit(); // This does the actual writing to EEPROM
Now every time we boot up, we get the next random number! Note a 32-bit number (well, truncated 16-bit ) is returned by nextrandom, so it is modulus-ed with NUM_TWEETS to return a number between zero and NUM_TWEETS-1. A bit of logic is used to make sure a different tweet_id to the previous id is selected.
Enjoy the randomness!
Comments
Post a Comment