Pages: [1]   Go Down
 Author Topic: Random Function based upon internal temp sensor  (Read 3935 times) 0 Members and 1 Guest are viewing this topic.
Global Moderator
Netherlands
Online
Shannon Member
Karma: 211
Posts: 13488
In theory there is no difference between theory and practice, however in practice there are many...
 « on: January 05, 2011, 09:50:19 am » Bigger Smaller Reset

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.

Code:
//
//    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);
}
 « Last Edit: January 05, 2011, 10:11:32 am by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Norway@Oslo
Offline
Edison Member
Karma: 12
Posts: 2033
loveArduino(true);
 « Reply #1 on: January 05, 2011, 09:59:54 am » Bigger Smaller Reset

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
 Logged

Global Moderator
Netherlands
Online
Shannon Member
Karma: 211
Posts: 13488
In theory there is no difference between theory and practice, however in practice there are many...
 « Reply #2 on: January 05, 2011, 10:09:31 am » Bigger Smaller Reset

Quote
Just my 2 bits
Only if they are random

(done)
 « Last Edit: January 05, 2011, 10:10:58 am by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

North Yorkshire, UK
Offline
Karma: 104
Posts: 5531
 « Reply #3 on: January 05, 2011, 10:37:21 am » Bigger Smaller Reset

Haha  ;D
Best use for it yet.
 Logged

Global Moderator
Dallas
Offline
Shannon Member
Karma: 197
Posts: 12744
 « Reply #4 on: January 05, 2011, 11:37:53 am » Bigger Smaller Reset

On which processors will the code work?
 Logged

North Yorkshire, UK
Offline
Karma: 104
Posts: 5531
 « Reply #5 on: January 05, 2011, 12:34:25 pm » Bigger Smaller Reset

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

Global Moderator
Netherlands
Online
Shannon Member
Karma: 211
Posts: 13488
In theory there is no difference between theory and practice, however in practice there are many...
 « Reply #6 on: January 05, 2011, 05:41:40 pm » Bigger Smaller Reset

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".

Code:
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 ...
 « Last Edit: January 05, 2011, 05:46:50 pm by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Dallas
Offline
Shannon Member
Karma: 197
Posts: 12744
 « Reply #7 on: January 06, 2011, 01:29:01 am » Bigger Smaller Reset

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

Global Moderator
Netherlands
Online
Shannon Member
Karma: 211
Posts: 13488
In theory there is no difference between theory and practice, however in practice there are many...
 « Reply #8 on: January 06, 2011, 02:54:39 am » Bigger Smaller Reset

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

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Dallas
Offline
Shannon Member
Karma: 197
Posts: 12744
 « Reply #9 on: January 06, 2011, 03:30:49 am » Bigger Smaller Reset

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.
 Logged

Global Moderator
Netherlands
Online
Shannon Member
Karma: 211
Posts: 13488
In theory there is no difference between theory and practice, however in practice there are many...
 « Reply #10 on: January 06, 2011, 05:56:39 am » Bigger Smaller Reset

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 - http://www.random.org/bytes/

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

 « Last Edit: January 06, 2011, 07:08:03 am by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Dallas
Offline
Shannon Member
Karma: 197
Posts: 12744
 « Reply #11 on: January 06, 2011, 12:55:43 pm » Bigger Smaller Reset

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
 Logged

Global Moderator
Netherlands
Online
Shannon Member
Karma: 211
Posts: 13488
In theory there is no difference between theory and practice, however in practice there are many...
 « Reply #12 on: January 07, 2011, 12:16:18 pm » Bigger Smaller Reset

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.

Code:
//
//    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);
}
 « Last Edit: January 07, 2011, 12:17:28 pm by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Netherlands
Online
Shannon Member
Karma: 211
Posts: 13488
In theory there is no difference between theory and practice, however in practice there are many...
 « Reply #13 on: January 07, 2011, 01:02:11 pm » Bigger Smaller Reset

Quote
If your application has an ethernetshield one could call - http://www.random.org/bytes/

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.

Code:
/*
*    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())
{
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()
 « Last Edit: January 07, 2011, 01:11:20 pm by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

 Pages: [1]   Go Up