Skip to content

Commit 5f77478

Browse files
authored
Replace PRNG with hardware RNG (#4225)
Both ESP8266 and ESP32 have a hardware random register. This update makes use of that. It is slightly faster than the fastled variants but mostly it is truly random, even when the timing limitations stated in the datasheet are disregarded. Also saves a bit on code size. - Replaced all random8() and random16() calls with new hw_random() versions - Not replaced in FX where PRNG is required
1 parent 07cc3aa commit 5f77478

File tree

7 files changed

+257
-227
lines changed

7 files changed

+257
-227
lines changed

wled00/FX.cpp

+159-161
Large diffs are not rendered by default.

wled00/colors.cpp

+47-47
Original file line numberDiff line numberDiff line change
@@ -124,86 +124,86 @@ void setRandomColor(byte* rgb)
124124
*/
125125
CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette)
126126
{
127-
CHSV palettecolors[4]; //array of colors for the new palette
128-
uint8_t keepcolorposition = random8(4); //color position of current random palette to keep
129-
palettecolors[keepcolorposition] = rgb2hsv(basepalette.entries[keepcolorposition*5]); //read one of the base colors of the current palette
130-
palettecolors[keepcolorposition].hue += random8(10)-5; // +/- 5 randomness of base color
131-
//generate 4 saturation and brightness value numbers
132-
//only one saturation is allowed to be below 200 creating mostly vibrant colors
133-
//only one brightness value number is allowed below 200, creating mostly bright palettes
134-
135-
for (int i = 0; i < 3; i++) { //generate three high values
136-
palettecolors[i].saturation = random8(200,255);
137-
palettecolors[i].value = random8(220,255);
127+
CHSV palettecolors[4]; // array of colors for the new palette
128+
uint8_t keepcolorposition = hw_random8(4); // color position of current random palette to keep
129+
palettecolors[keepcolorposition] = rgb2hsv(basepalette.entries[keepcolorposition*5]); // read one of the base colors of the current palette
130+
palettecolors[keepcolorposition].hue += hw_random8(10)-5; // +/- 5 randomness of base color
131+
// generate 4 saturation and brightness value numbers
132+
// only one saturation is allowed to be below 200 creating mostly vibrant colors
133+
// only one brightness value number is allowed below 200, creating mostly bright palettes
134+
135+
for (int i = 0; i < 3; i++) { // generate three high values
136+
palettecolors[i].saturation = hw_random8(200,255);
137+
palettecolors[i].value = hw_random8(220,255);
138138
}
139-
//allow one to be lower
140-
palettecolors[3].saturation = random8(20,255);
141-
palettecolors[3].value = random8(80,255);
139+
// allow one to be lower
140+
palettecolors[3].saturation = hw_random8(20,255);
141+
palettecolors[3].value = hw_random8(80,255);
142142

143-
//shuffle the arrays
143+
// shuffle the arrays
144144
for (int i = 3; i > 0; i--) {
145-
std::swap(palettecolors[i].saturation, palettecolors[random8(i + 1)].saturation);
146-
std::swap(palettecolors[i].value, palettecolors[random8(i + 1)].value);
145+
std::swap(palettecolors[i].saturation, palettecolors[hw_random8(i + 1)].saturation);
146+
std::swap(palettecolors[i].value, palettecolors[hw_random8(i + 1)].value);
147147
}
148148

149-
//now generate three new hues based off of the hue of the chosen current color
149+
// now generate three new hues based off of the hue of the chosen current color
150150
uint8_t basehue = palettecolors[keepcolorposition].hue;
151-
uint8_t harmonics[3]; //hues that are harmonic but still a little random
152-
uint8_t type = random8(5); //choose a harmony type
151+
uint8_t harmonics[3]; // hues that are harmonic but still a little random
152+
uint8_t type = hw_random8(5); // choose a harmony type
153153

154154
switch (type) {
155155
case 0: // analogous
156-
harmonics[0] = basehue + random8(30, 50);
157-
harmonics[1] = basehue + random8(10, 30);
158-
harmonics[2] = basehue - random8(10, 30);
156+
harmonics[0] = basehue + hw_random8(30, 50);
157+
harmonics[1] = basehue + hw_random8(10, 30);
158+
harmonics[2] = basehue - hw_random8(10, 30);
159159
break;
160160

161161
case 1: // triadic
162-
harmonics[0] = basehue + 113 + random8(15);
163-
harmonics[1] = basehue + 233 + random8(15);
164-
harmonics[2] = basehue - 7 + random8(15);
162+
harmonics[0] = basehue + 113 + hw_random8(15);
163+
harmonics[1] = basehue + 233 + hw_random8(15);
164+
harmonics[2] = basehue - 7 + hw_random8(15);
165165
break;
166166

167167
case 2: // split-complementary
168-
harmonics[0] = basehue + 145 + random8(10);
169-
harmonics[1] = basehue + 205 + random8(10);
170-
harmonics[2] = basehue - 5 + random8(10);
168+
harmonics[0] = basehue + 145 + hw_random8(10);
169+
harmonics[1] = basehue + 205 + hw_random8(10);
170+
harmonics[2] = basehue - 5 + hw_random8(10);
171171
break;
172172

173173
case 3: // square
174-
harmonics[0] = basehue + 85 + random8(10);
175-
harmonics[1] = basehue + 175 + random8(10);
176-
harmonics[2] = basehue + 265 + random8(10);
174+
harmonics[0] = basehue + 85 + hw_random8(10);
175+
harmonics[1] = basehue + 175 + hw_random8(10);
176+
harmonics[2] = basehue + 265 + hw_random8(10);
177177
break;
178178

179179
case 4: // tetradic
180-
harmonics[0] = basehue + 80 + random8(20);
181-
harmonics[1] = basehue + 170 + random8(20);
182-
harmonics[2] = basehue - 15 + random8(30);
180+
harmonics[0] = basehue + 80 + hw_random8(20);
181+
harmonics[1] = basehue + 170 + hw_random8(20);
182+
harmonics[2] = basehue - 15 + hw_random8(30);
183183
break;
184184
}
185185

186-
if (random8() < 128) {
187-
//50:50 chance of shuffling hues or keep the color order
186+
if (hw_random8() < 128) {
187+
// 50:50 chance of shuffling hues or keep the color order
188188
for (int i = 2; i > 0; i--) {
189-
std::swap(harmonics[i], harmonics[random8(i + 1)]);
189+
std::swap(harmonics[i], harmonics[hw_random8(i + 1)]);
190190
}
191191
}
192192

193-
//now set the hues
193+
// now set the hues
194194
int j = 0;
195195
for (int i = 0; i < 4; i++) {
196-
if (i==keepcolorposition) continue; //skip the base color
196+
if (i==keepcolorposition) continue; // skip the base color
197197
palettecolors[i].hue = harmonics[j];
198198
j++;
199199
}
200200

201201
bool makepastelpalette = false;
202-
if (random8() < 25) { //~10% chance of desaturated 'pastel' colors
202+
if (hw_random8() < 25) { // ~10% chance of desaturated 'pastel' colors
203203
makepastelpalette = true;
204204
}
205205

206-
//apply saturation & gamma correction
206+
// apply saturation & gamma correction
207207
CRGB RGBpalettecolors[4];
208208
for (int i = 0; i < 4; i++) {
209209
if (makepastelpalette && palettecolors[i].saturation > 180) {
@@ -219,12 +219,12 @@ CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette)
219219
RGBpalettecolors[3]);
220220
}
221221

222-
CRGBPalette16 generateRandomPalette() //generate fully random palette
222+
CRGBPalette16 generateRandomPalette() // generate fully random palette
223223
{
224-
return CRGBPalette16(CHSV(random8(), random8(160, 255), random8(128, 255)),
225-
CHSV(random8(), random8(160, 255), random8(128, 255)),
226-
CHSV(random8(), random8(160, 255), random8(128, 255)),
227-
CHSV(random8(), random8(160, 255), random8(128, 255)));
224+
return CRGBPalette16(CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)),
225+
CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)),
226+
CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)),
227+
CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)));
228228
}
229229

230230
void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB (32bit with white = 0)

wled00/fcn_declare.h

+23
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,12 @@ void userConnected();
458458
void userLoop();
459459

460460
//util.cpp
461+
#ifdef ESP8266
462+
#define HW_RND_REGISTER RANDOM_REG32
463+
#else // ESP32 family
464+
#include "soc/wdev_reg.h"
465+
#define HW_RND_REGISTER REG_READ(WDEV_RND_REG)
466+
#endif
461467
int getNumVal(const String* req, uint16_t pos);
462468
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
463469
bool getVal(JsonVariant elem, byte* val, byte minv=0, byte maxv=255); // getVal supports inc/decrementing and random ("X~Y(r|~[w][-][Z])" form)
@@ -485,6 +491,23 @@ void enumerateLedmaps();
485491
uint8_t get_random_wheel_index(uint8_t pos);
486492
float mapf(float x, float in_min, float in_max, float out_min, float out_max);
487493

494+
// fast (true) random numbers using hardware RNG, all functions return values in the range lowerlimit to upperlimit-1
495+
// note: for true random numbers with high entropy, do not call faster than every 200ns (5MHz)
496+
// tests show it is still highly random reading it quickly in a loop (better than fastled PRNG)
497+
// for 8bit and 16bit random functions: no limit check is done for best speed
498+
// 32bit inputs are used for speed and code size, limits don't work if inverted or out of range
499+
// inlining does save code size except for random(a,b) and 32bit random with limits
500+
#define random hw_random // replace arduino random()
501+
inline uint32_t hw_random() { return HW_RND_REGISTER; };
502+
uint32_t hw_random(uint32_t upperlimit); // not inlined for code size
503+
int32_t hw_random(int32_t lowerlimit, int32_t upperlimit);
504+
inline uint16_t hw_random16() { return HW_RND_REGISTER; };
505+
inline uint16_t hw_random16(uint32_t upperlimit) { return (hw_random16() * upperlimit) >> 16; }; // input range 0-65535 (uint16_t)
506+
inline int16_t hw_random16(int32_t lowerlimit, int32_t upperlimit) { int32_t range = upperlimit - lowerlimit; return lowerlimit + hw_random16(range); }; // signed limits, use int16_t ranges
507+
inline uint8_t hw_random8() { return HW_RND_REGISTER; };
508+
inline uint8_t hw_random8(uint32_t upperlimit) { return (hw_random8() * upperlimit) >> 8; }; // input range 0-255
509+
inline uint8_t hw_random8(uint32_t lowerlimit, uint32_t upperlimit) { uint32_t range = upperlimit - lowerlimit; return lowerlimit + hw_random8(range); }; // input range 0-255
510+
488511
// RAII guard class for the JSON Buffer lock
489512
// Modeled after std::lock_guard
490513
class JSONBufferGuard {

wled00/ir.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ static void decodeIRJson(uint32_t code)
593593
decBrightness();
594594
} else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback
595595
uint8_t p1 = fdo["PL"] | 1;
596-
uint8_t p2 = fdo["FX"] | random8(strip.getModeCount() -1);
596+
uint8_t p2 = fdo["FX"] | hw_random8(strip.getModeCount() -1);
597597
uint8_t p3 = fdo["FP"] | 0;
598598
presetFallback(p1, p2, p3);
599599
}

wled00/remote.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ static bool remoteJson(int button)
146146
parsed = true;
147147
} else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback
148148
uint8_t p1 = fdo["PL"] | 1;
149-
uint8_t p2 = fdo["FX"] | random8(strip.getModeCount() -1);
149+
uint8_t p2 = fdo["FX"] | hw_random8(strip.getModeCount() -1);
150150
uint8_t p3 = fdo["FP"] | 0;
151151
presetWithFallback(p1, p2, p3);
152152
parsed = true;

wled00/util.cpp

+24-9
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ int getNumVal(const String* req, uint16_t pos)
1414
void parseNumber(const char* str, byte* val, byte minv, byte maxv)
1515
{
1616
if (str == nullptr || str[0] == '\0') return;
17-
if (str[0] == 'r') {*val = random8(minv,maxv?maxv:255); return;} // maxv for random cannot be 0
17+
if (str[0] == 'r') {*val = hw_random8(minv,maxv?maxv:255); return;} // maxv for random cannot be 0
1818
bool wrap = false;
1919
if (str[0] == 'w' && strlen(str) > 1) {str++; wrap = true;}
2020
if (str[0] == '~') {
@@ -474,29 +474,29 @@ um_data_t* simulateSound(uint8_t simulationId)
474474
break;
475475
case UMS_WeWillRockYou:
476476
if (ms%2000 < 200) {
477-
volumeSmth = random8(255);
477+
volumeSmth = hw_random8();
478478
for (int i = 0; i<5; i++)
479-
fftResult[i] = random8(255);
479+
fftResult[i] = hw_random8();
480480
}
481481
else if (ms%2000 < 400) {
482482
volumeSmth = 0;
483483
for (int i = 0; i<16; i++)
484484
fftResult[i] = 0;
485485
}
486486
else if (ms%2000 < 600) {
487-
volumeSmth = random8(255);
487+
volumeSmth = hw_random8();
488488
for (int i = 5; i<11; i++)
489-
fftResult[i] = random8(255);
489+
fftResult[i] = hw_random8();
490490
}
491491
else if (ms%2000 < 800) {
492492
volumeSmth = 0;
493493
for (int i = 0; i<16; i++)
494494
fftResult[i] = 0;
495495
}
496496
else if (ms%2000 < 1000) {
497-
volumeSmth = random8(255);
497+
volumeSmth = hw_random8();
498498
for (int i = 11; i<16; i++)
499-
fftResult[i] = random8(255);
499+
fftResult[i] = hw_random8();
500500
}
501501
else {
502502
volumeSmth = 0;
@@ -516,7 +516,7 @@ um_data_t* simulateSound(uint8_t simulationId)
516516
break;
517517
}
518518

519-
samplePeak = random8() > 250;
519+
samplePeak = hw_random8() > 250;
520520
FFT_MajorPeak = 21 + (volumeSmth*volumeSmth) / 8.0f; // walk thru full range of 21hz...8200hz
521521
maxVol = 31; // this gets feedback fro UI
522522
binNum = 8; // this gets feedback fro UI
@@ -582,7 +582,7 @@ void enumerateLedmaps() {
582582
uint8_t get_random_wheel_index(uint8_t pos) {
583583
uint8_t r = 0, x = 0, y = 0, d = 0;
584584
while (d < 42) {
585-
r = random8();
585+
r = hw_random8();
586586
x = abs(pos - r);
587587
y = 255 - x;
588588
d = MIN(x, y);
@@ -594,3 +594,18 @@ uint8_t get_random_wheel_index(uint8_t pos) {
594594
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
595595
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
596596
}
597+
598+
// 32 bit random number generator, inlining uses more code, use hw_random16() if speed is critical (see fcn_declare.h)
599+
uint32_t hw_random(uint32_t upperlimit) {
600+
uint32_t rnd = hw_random();
601+
uint64_t scaled = uint64_t(rnd) * uint64_t(upperlimit);
602+
return scaled >> 32;
603+
}
604+
605+
int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) {
606+
if(lowerlimit >= upperlimit) {
607+
return lowerlimit;
608+
}
609+
uint32_t diff = upperlimit - lowerlimit;
610+
return hw_random(diff) + lowerlimit;
611+
}

wled00/wled.cpp

+2-8
Original file line numberDiff line numberDiff line change
@@ -544,14 +544,8 @@ void WLED::setup()
544544
#endif
545545

546546
// Seed FastLED random functions with an esp random value, which already works properly at this point.
547-
#if defined(ARDUINO_ARCH_ESP32)
548-
const uint32_t seed32 = esp_random();
549-
#elif defined(ARDUINO_ARCH_ESP8266)
550-
const uint32_t seed32 = RANDOM_REG32;
551-
#else
552-
const uint32_t seed32 = random(std::numeric_limits<long>::max());
553-
#endif
554-
random16_set_seed((uint16_t)((seed32 & 0xFFFF) ^ (seed32 >> 16)));
547+
const uint32_t seed32 = hw_random();
548+
random16_set_seed((uint16_t)seed32);
555549

556550
#if WLED_WATCHDOG_TIMEOUT > 0
557551
enableWatchdog();

0 commit comments

Comments
 (0)