Random Function based upon internal temp sensor

Inspired by a discussion over the internal temperature sensor - http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1294205576 - and it outputting only rubbish I had the idea to use it for a random generator or for a random seed generator.

The code makes 32 calls to the internal temp sensor and strips of the "MNB = most noisy bit" and builds up a return value. Variations are possible, especially by adding a parameter representing the number of calls.

It generates quite random output on my UNO, but I haven't tested it on other platforms. I have all my 328's occupied in projects ... Furthermore I don't knw the quality of the randomness, but it seems to behave random enough for me. The speed seems a bit slow to me - no figures yet - therefor I think it is more usefull as a random seed generator than as a pure random generator.

Remarks, improvements, comments all welcome !

Todo:

  • Test the distribution / randomness ??
  • Test speed
  • Wrap in a class
  • Playground article.
// 
//    FILE: RealRandom
//  AUTHOR: Rob Tillaart
// PURPOSE: Simple Random functions based upon unreliable internal temp sensor
// VERSION: 0.1
//       DATE: 2011-05-01
//
// Released to the public domain, use at own risk
//

long longRandom()
{
  analogReference(INTERNAL);
  unsigned long rv = 0;
  for (byte i=0; i< 32; i++) rv |= (analogRead(8) & 1L) << i;
  return rv;
}

float floatRandom()
{
  analogReference(INTERNAL);
  float rv = 0;
  float t = 0.5;
  for (byte i=0; i<32; i++)
  {
    if (analogRead(8) & 1L) rv += t;
    t /= 2;
  }
  return rv;
}

long randomRange(long _min, long _max)
{
  return _min + long ((_max - _min) * floatRandom()); 
}

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  long t = randomRange(0,10);
  Serial.println(t);
  
  t = longRandom();
  Serial.println(t);
  
  float f = floatRandom();
  Serial.println(f);
  delay(500);  
}

Cool!

I would recommend you to adhere to the naming convention of Arduino that is lowerUpper camel casing.

longRandom, floatRandom, randomRanged

Just my 2 bits

Just my 2 bits

Only if they are random :wink:

(done)

Haha ;D
Best use for it yet.

On which processors will the code work?

ATmega328P in DIP, possibly others but the duemilanove and uno will do it at least.

Wrote a frequency test - change loop() in program above shows that on my UNO there are four numbers - 0, 13, 26, 39 - are more preferred than others. The others are more distributed evenly. So it is definitely not as random as I would like it to be, but very interesting why.

Hypothesis: Think it is because the same sensor is read out in a tight loop and the noise bit fluctuates not fast enough, resulting in similar noise bits => the bits are not random enough (entropy/randomness).
in other words the readings are "too stable".

void loop()
{
  for (int i=1; i< 25; i++) frequency_test(40);
}

void frequency_test(int mx)
{
  int nr[40];  
  for(int i = 0; i < 40; i++) nr[i] = 0;

  for(int i = 0; i < 1000; i++) nr[ randomRange(0, mx)] ++;

  Serial.println();
  Serial.println(mx);
  Serial.println("=======================");
  for(int i = 0; i < 40; i++)
  {
    Serial.print(i);
    Serial.print("\t");
    Serial.print(nr[i]);;
    Serial.print("\t");
    for (int x=0; x < nr[i]; x++) Serial.print("]");
    Serial.println();
  }
}

output sample

40
=======================
0       36      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
 1       31      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
 2       29      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
 3       24      ]]]]]]]]]]]]]]]]]]]]]]]]
 4       24      ]]]]]]]]]]]]]]]]]]]]]]]]
 5       21      ]]]]]]]]]]]]]]]]]]]]]
 6       27      ]]]]]]]]]]]]]]]]]]]]]]]]]]]
 7       19      ]]]]]]]]]]]]]]]]]]]
 8       21      ]]]]]]]]]]]]]]]]]]]]]
 9       34      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
10      13      ]]]]]]]]]]]]]
11      23      ]]]]]]]]]]]]]]]]]]]]]]]
12      21      ]]]]]]]]]]]]]]]]]]]]]
13      43      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
14      30      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
15      16      ]]]]]]]]]]]]]]]]
16      18      ]]]]]]]]]]]]]]]]]]
17      26      ]]]]]]]]]]]]]]]]]]]]]]]]]]
18      11      ]]]]]]]]]]]
19      30      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
20      26      ]]]]]]]]]]]]]]]]]]]]]]]]]]
21      18      ]]]]]]]]]]]]]]]]]]
22      25      ]]]]]]]]]]]]]]]]]]]]]]]]]
23      20      ]]]]]]]]]]]]]]]]]]]]
24      15      ]]]]]]]]]]]]]]]
25      23      ]]]]]]]]]]]]]]]]]]]]]]]
26      59      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
27      35      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
28      23      ]]]]]]]]]]]]]]]]]]]]]]]
29      25      ]]]]]]]]]]]]]]]]]]]]]]]]]
30      27      ]]]]]]]]]]]]]]]]]]]]]]]]]]]
31      12      ]]]]]]]]]]]]
32      27      ]]]]]]]]]]]]]]]]]]]]]]]]]]]
33      17      ]]]]]]]]]]]]]]]]]
34      19      ]]]]]]]]]]]]]]]]]]]
35      20      ]]]]]]]]]]]]]]]]]]]]
36      23      ]]]]]]]]]]]]]]]]]]]]]]]
37      27      ]]]]]]]]]]]]]]]]]]]]]]]]]]]
38      19      ]]]]]]]]]]]]]]]]]]]
39      43      ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]

To be continued ...

Do you have a capacitor on AREF (to GND)?

No cap on AREF, just an UNO with an ethernetshield (latter not used).

I think you're going to have a difficult time using using the internal temperature sensor as an entropy source. When I added a capacitor to AREF, the "randomness" was noticeably less. When I used "noise reduction sleep mode", the "randomness" essentially disappeared.

I suspect the "randomness" is a by-product of digital noise from the processor itself. The datasheet mentions many times the need to reduce / eliminate / avoid digital noise when performing an A/D conversion.

I can think of two problems that result from using "digital noise" as an entropy source...

  1. The "noise" is not at all random. The processor is executing the exact same code every time a random bit is needed. By executing the same code each time, the "noise" is the same each time. This is bound to create patterns in the readings.

  2. Holding my hand near AREF seems to introduce enough capacitance to reduce the randomness / noise. I suspect other things (wire, PCB traces, whatever) will have the same effect. This makes the technique unreliable.

For what it's worth, I really hope I'm wrong. The Arduino sorely needs an entropy source. The need for "truly random" values has come up many times.

Thanks for the info, it is a pity the sensor is not noisy enough as the search for true randomness still continues

If your application has an ethernetshield one could call - RANDOM.ORG - Byte Generator

TODO: built an Arduino wrapper around this webform....

  • fetch 4 bytes to make a long to be used
    ** in randomSeed()
    ** to generate a random
    ** to generate a float [0..1>
    ** to generate a number between n and M (M is bigger than n)

think I have found a thing todo today :slight_smile:

They have a form for floating-point numbers. You'll have to check for and exclude 1.00000000...

http://www.random.org/decimal-fractions/?num=1&dec=8&col=1&format=plain&rnd=new

Although the random function based upon the internal clock is not random enough I still did the speed test to get an impression of its performance. The sketch below produced timing results in micros for 1000 calls.

1000 * random used : 94568 (<0.01 sec)      
1000 * longRandom used : 3802480  (3.8 sec)
1000 * floatRandom used : 3981380  (4.0 sec)

longRandom() and floatRandom() are 40++ times slower than the native random() function. That is another indication it is more suitable as seedgenerator than as random function itself.

// 
//    FILE: RealRandom
//  AUTHOR: Rob Tillaart
// PURPOSE: Simple Random functions based upon unreliable internal temp sensor
// VERSION: 0.1
//
// Released to the public domain, use at own risk
//

long longRandom()
{
  analogReference(INTERNAL);
  unsigned long rv = 0;
  for (byte i=0; i< 32; i++) rv |= (analogRead(8) & 1L) << i;
  return rv;
}

float floatRandom()
{
  analogReference(INTERNAL);
  float rv = 0;
  float t = 0.5;
  for (byte i=0; i<32; i++)
  {
    if (analogRead(8) & 1L) rv += t;
    t /= 2.0;
    //delay(1); // improves flatness but costs 32 mills
  }
  return rv;
}

long randomRange(long _min, long _max)
{
  return _min + long ((_max - _min) * floatRandom() ); 
}

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  long x;
  float f;
  unsigned long start;
  unsigned long time;
 
  start = micros();
  for (int i=1; i< 1000; i++)
  {
    x = random();
  }
  time = micros() - start;
  Serial.print("1000 * random used : ");
  Serial.println(time);
  
  start = micros();
  for (int i=1; i< 1000; i++)
  {
    x = longRandom();
  }
  time = micros() - start;
  Serial.print("1000 * longRandom used : ");
  Serial.println(time); 
  
    start = micros();
  for (int i=1; i< 1000; i++)
  {
    f = floatRandom();
  }
  time = micros() - start;
  Serial.print("1000 * floatRandom used : ");
  Serial.println(time);
  
  while (1);
}

If your application has an ethernetshield one could call - RANDOM.ORG - Byte Generator

And so I build this prototype . It is a simple webclient that asks for 4 bytes and converts this output to a long. If the network connection fails the internal random() function is called as a backup.

/*
 *    FILE: randomNet
 *  AUTHOR: Rob van den Tillaart
 *    DATE: 2011-01-06 
 * VERSION: 0.2
 * PURPOSE: fetch a random long over the net from www.random.org
 */

#include <SPI.h>
#include <Ethernet.h>

byte mac[6] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[4] = { 192, 168, 1, 25 };
byte server[4] = { 174, 143, 173, 125 };  // nslookup www.random.org 

long fetchRandomLong()
{
  long rv = -1;
  byte ch;
  int state = 0;
  int x = 0;
  
  Client client(server, 80);

  if (client.connect()) 
  {
    // client.println("GET /decimal-fractions/?num=1&dec=8&col=1&format=plain&rnd=new");
    client.println("GET /integers/?num=4&min=0&max=255&col=1&base=10&format=plain&rnd=new");

    // wait max 1 second to read the answer
    for (int i=0; i< 1000; i++)
    {
      if (client.available()) break;
      delay(1);
    }
    // fetch the string
    while (client.available())
    {
      ch = client.read();
      if ('0' <= ch && ch <= '9')
      {
        state = 1;
        x = x*10 + ch - '0';
      }
      else 
      {
        if (state == 1)
        {
          state = 0;
          rv <<= 8;
          rv += x;
          x = 0;
        }
      }
    }
    client.stop();
    for (int i=0; i<100; i++)
    {
      if (client.status() == 0) break;
      delay(1);
    }
  }
  //  network did not work => use internal as backup
  if (rv == -1)     
  {
    rv = random();
  }
  return rv;
}

////////////////////////////////////////////////////////
void setup() 
{
  Serial.begin(115200);
  Serial.println("Demo - random from www.random.org");
  Ethernet.begin(mac, ip);
  delay(1000);
}

void loop() 
{
  unsigned long start = millis();
  long rd = fetchRandomLong();
  unsigned long timeUsed = millis() - start;
  Serial.print("random : ");
  Serial.println(rd);
  Serial.print("time : ");
  Serial.println(timeUsed);
}
// END OF FILE

Sample of the output

Demo Random via net
random : 1346747051
time : 1033
random : -1992743329
time : 705
random : -629485075
time : 795
random : 491946319
time : 532
random : -1748141667
time : 623
random : -1086788461
time : 1096
random : 907126690
time : 1187
random : -1270226305
time : 911
random : 605004460
time : 851
random : -1837384756
time : 692

The randomness of the numbers is guaranteed by the organization behind the webserver www.random.org. The timing is almost a random generator in itself as it differs quite alot. Average timing varies around 750-800 millis(). The code size is 6840 (IDE 21 - UNO).

The code is bigger and slower than the random function based upon the internal temperature sensor ans so we can conclude that true randomness has its price.

TODO:

  • a minimal improvement would be ask for two 16 bit numbers
  • fetchRandomFloat()