#include <Adafruit_NeoPixel.h>
class Scrambler
{
private:
uint32_t ScrambleSeed = 0;
// default values recommended by http://isthe.com/chongo/tech/comp/fnv/
const uint32_t Prime = 0x01000193; // 16777619
const uint32_t Seed = 0x811C9DC5; // 2166136261
uint32_t PreSeed = Seed;
// https://create.stephan-brumme.com/fnv-hash/
const uint32_t fnv1a(const uint8_t oneByte, uint32_t hash)
{
return Prime * (oneByte ^ hash);
}
public:
void SetSeedArray(const uint8_t seed[sizeof(uint32_t)])
{
//ScrambleSeed = seed[0] | (((uint32_t)seed[1]) << 8) | (((uint32_t)seed[2]) << 16) | (((uint32_t)seed[3]) << 24);
const uint32_t value = (seed[0] | (((uint32_t)seed[1]) << 8) | (((uint32_t)seed[2]) << 16) | (((uint32_t)seed[3]) << 24));
ScrambleSeed = Prime ^ value;
ScrambleSeed ^= value << 3;
ScrambleSeed ^= value >> 7;
// Apply xor shift.
ScrambleSeed ^= ScrambleSeed << 13;
ScrambleSeed ^= ScrambleSeed >> 17;
ScrambleSeed ^= ScrambleSeed << 5;
PreSeed = fnv1a(value, Seed);
}
void SetSeed(const uint32_t seed)
{
SetSeedArray((const uint8_t*)&seed);
}
const uint8_t SeededHash2(const uint32_t value)
{
uint32_t state = PreSeed;
state = fnv1a(value, state);
state = fnv1a(value >> 8, state);
state = fnv1a(value >> 16, state);
return fnv1a(value >> 24, state);
}
const uint8_t SeededHash(const uint32_t value)
{
uint32_t state = ScrambleSeed ^ value;
state ^= value << 3;
state ^= value >> 7;
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
return ((uint8_t)state);
}
};
static const int pixelPin = 13;
static const int n_leds = 126;
static const int Period = 200;
Adafruit_NeoPixel ring = Adafruit_NeoPixel(n_leds, pixelPin);
Scrambler hasher{};
uint32_t TestIndex = 0;
uint8_t Accumulator[n_leds];
void setup()
{
ring.begin();
}
void loop()
{
hasher.SetSeed(1337);
accumulate(hasher.SeededHash2(TestIndex++));
delay(Period);
}
void accumulate(const uint8_t index)
{
const uint32_t on = ring.Color(UINT8_MAX, UINT8_MAX, UINT8_MAX);
const uint8_t channel = GetRealChannel<n_leds>(index);
if (Accumulator[channel] < UINT8_MAX)
{
Accumulator[channel]++;
}
else
{
for (int i = 0; i < n_leds; i++)
{
if ((i != (channel))
&& (Accumulator[channel] > 0))
{
Accumulator[i]--;
}
}
}
for (int i = 0; i < n_leds; i++)
{
const uint8_t charge = Accumulator[i];
if (charge >= UINT8_MAX)
{
ring.setPixelColor(i, ring.Color(UINT8_MAX, UINT8_MAX, 0));
}
else
{
ring.setPixelColor(i, ring.Color(charge, 0, 0));
}
}
ring.setPixelColor(channel, on);
ring.show();
}
template<const uint8_t ChannelCount>
constexpr uint8_t GetRealChannel(const uint8_t abstractChannel)
{
return ((uint16_t)abstractChannel * (ChannelCount-1)) / (UINT8_MAX-0);
}