-1

I am coding a roulette game in arduino's C++ based environment that flashes LEDs to represent the ball circling and stopping. My problem is that the ball stops at the same location every time. I am using randomSeed(analogRead(0)) combined with random(min,max) to generate a random number. This random number represents where my ball "enters". However when I reset my arduino, I get the same exact results. Does anyone know where my problem may lie?

Some important notes

-I am calling random in the global scope because I want a single random starting point for my roulette game (where the "ball" enters). Would having randomSeed in the setup and random in the global scope cause a problem when these two work together? I am new to how "setup" interacts with the rest of the code in arduino.

-The random number I am generating is in a very small range (0-4). However I have reset my arduino many times and gotten 2 as the first number about 30 consecutive times.

I've also tried calling randomSeed in the global scope instead of setup but I get hit with the following error when I do so. " exit status 1 expected constructor, destructor, or type conversion before '(' token "

If there is any more details I can provide or if anyone knows of a post on a similar issue please let me know.

Arduino Code tends to have 3 parts. The setup, loop, and outside both. I've ran some tests and found that changes to randomSeed() do not affect the numbers generated by random when random is outside both loop and setup. However when random is in loop, it reacts to changes from randomSeed. Why is that? Is there a way I can get random and randomSeed to work together when randomSeed is in setup and random is called in global?

Jake Sak
  • 40
  • 7
  • 4
    The value is not random, it's "intermediate". It will be the same on each run with the same firmware. Read about random number generations in embedded environments. `"exit status 1 expected constructor, destructor, or type conversion before '(' token "` is a syntax error. until you show your code, no-one will know what the error is. – KamilCuk Dec 27 '18 at 05:21
  • And your Arduino code is **C++**, not C. – Antti Haapala -- Слава Україні Dec 27 '18 at 06:04
  • Thank you, I updated my post. Is there any way to generate non repeating numbers upon reset on an embedded system? – Jake Sak Dec 27 '18 at 06:09
  • the suggestion for `randomSeed` is to use a variable number - and then said is that `analogRead` with an *unconnected pin* might give such a number. – Antti Haapala -- Слава Україні Dec 27 '18 at 06:12
  • I've ran more tests and found that changes to the number I give randomSeed do not change the first number generated by random in my program, but they do in a test program I wrote. The only difference between the 2 cases is I call random in the global name space in the buggy program, while I call random in my void loop in the program that does react to changes in randomSeed. Is there a way to get this same variance when declaring in the global namespace (outside setup and loop)? – Jake Sak Dec 27 '18 at 06:58
  • 1
    add some code to the question – Juraj Dec 27 '18 at 11:42
  • "*I am calling random in the global scope*" What do you mean "global scope"? You can't call functions outside of functions. – gre_gor Dec 27 '18 at 16:08

1 Answers1

2

Here is my advice on how to implement a random number generator on Arduino, or indeed any microcontroller:

  1. Seed the random number generator at bootup (in setup() in Arduino) only.
     

  2. Use a timer to consume random numbers, and/or consume random numbers when idle.

    This way, even though the state immediately after power on is predictable, the state changes very rapidly, and becomes unpredictable (unless the microcontroller state is monitored very closely at something like microsecond intervals).
     

  3. Use exclusive-OR (^) to mix in entropy from sources that have at least some randomness, like say a hardware source based on avalanche noise.

    For best results, do it only occasionally. I would personally feed the entropy to a separate pseudorandom number generator, and mix its output to the primary generator state.
     

  4. Use human input (button presses et cetera, via high-resolution timers) as a source for entropy.

    That is, measure the time between e.g. successive button presses at a very high resolution, and use a few least significant bits as entropy to mix into the pseudorandom number generator state.

    After just a few button presses, the internal state of the pseudorandom number generator would basically be unpredictable.
     

  5. Use battery-backed SRAM (often available as part of real-time clock modules) to store a seed state for the next bootup.

    Technically, you could also use EEPROM or Flash for this, but since they have a limited number of write cycles, I do not recommend using those for this.
     

As I've mentioned in other posts, I personally do not trust the built-in pseudorandom number generators, and instead implement one of the known linear feedback shift generator based ones (a Xorshift variant, or Mersenne Twister). They are well known and their output randomness characterized.

For an Arduino-based game, I'd probably use Xorshift128 for the random number generator itself, and a Xorshift64* for obtaining entropy from hardware sources and using its output to perturb the main generator state every now and then (say, at button press, or similar occasions; less than once per second).

I would ensure that even when idle, the generators' states are advanced (by "consuming" pseudorandom numbers; just throwing them out), so that physical world timing would become a major factor in the sequences generated.

Various microcontrollers have different timers and hardware features available, so the exact code I would implement definitely depends on the hardware used. Unfortunately, that means the code is not that portable across hardware; even small, innocuous-looking changes may mean the output becomes quite predictable. I could write an example for Arduino Leonardo / Arduino Pro Micro (both based on ATmega32u4 microcontroller), because I have one at hand, but if you are using some other microcontroller, the code might be interesting, but misleading at worst if you tried to port it to another hardware architecture without knowing its details and behaviour.


To generate pseudorandom integers within a range, I recommend using the exclusion method, rather than the modulo (%) method. The exclusion method ensures uniform distribution; the modulo method can give a small bias to some values near the smaller end of the range.

The idea of the exclusion method is simple. You grab exactly the needed number of bits to cover the range, but exclude values outside the range. On average, you may consume up to twice as many random number bits, but with a fast generator like Xorshift, that is definitely not a problem.

The general pattern of a fixed 32-bit signed integer range function is

static inline int32_t  rndrange(void)
{
    uint32_t  u;
    do {
        u = random32() >> SHIFT;
    } while (u > LIMIT);
    return u + MINIMUM;
}

where random32() returns uniform pseudorandom 32-bit unsigned integers, MINIMUM is the smallest integer the function can return, MINIMUM + LIMIT is the maximum integer the function can return (LIMIT = MAXIMUM - MINIMUM), and SHIFT is

31 if LIMIT == 1      ║  15    LIMIT <= 131071
30    LIMIT <= 3      ║  14    LIMIT <= 262143
29    LIMIT <= 7      ║  13    LIMIT <= 524287
28    LIMIT <= 15     ║  12    LIMIT <= 1048575
27    LIMIT <= 31     ║  11    LIMIT <= 2097151
26    LIMIT <= 63     ║  10    LIMIT <= 4194303
25    LIMIT <= 127    ║   9    LIMIT <= 8388607
24    LIMIT <= 255    ║   8    LIMIT <= 16777215
23    LIMIT <= 511    ║   7    LIMIT <= 33554431
22    LIMIT <= 1023   ║   6    LIMIT <= 67108863
21    LIMIT <= 2047   ║   5    LIMIT <= 134217727
20    LIMIT <= 4095   ║   4    LIMIT <= 268435455
19    LIMIT <= 8191   ║   3    LIMIT <= 536870911
18    LIMIT <= 16383  ║   2    LIMIT <= 1073741823
17    LIMIT <= 32767  ║   1    LIMIT <= 2147483647
16    LIMIT <= 65535  ║   0 if LIMIT <= 4294967295

In Arduino, or any C or C++ code compiled using GCC, you can use

static inline int  random_intrange(const int  minval,
                                   const int  maxval)
{
    if (maxval > minval) {
        const unsigned int   limit = maxval - minval;
        const unsigned char  shift = __builtin_clz(limit);
        unsigned int         u;
        do {
            u = random_unsigned_int() >> shift;
        } while (u > limit);
        return minval + u;
    } else
        return minval;
}

static inline unsigned int  random_uintrange(const unsigned int  minval,
                                             const unsigned int  maxval)
{
    if (maxval > minval) {
        const unsigned int   limit = maxval - minval;
        const unsigned char  shift = __builtin_clz(limit);
        unsigned int         u;
        do {
            u = random_unsigned_int() >> shift;
        } while (u > limit);
        return minval + u;
    } else
        return minval;
}

as long as random_unsigned_int() is an uniform pseudorandom number generator that returns unsigned ints, from 0 to UINT_MAX, inclusive.

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • Thanks a ton for the advice! I am new to working with arduinos so you've done me a huge favor in giving me new paths to explore. I'll be sure to come back and give you an upvote once I earn enough stack overflow reputation to do so! – Jake Sak Dec 27 '18 at 07:24