#include <digitalWriteFast.h>
////////////////////////////////////////////////////
// Global Definitions
////////////////////////////////////////////////////
//
// Class for a debounced push button input.
//
class Button {
public:
Button(const uint8_t pin, bool as = LOW) : pin {pin}, activeState {as} {}
void begin() {
buttonState = !activeState;
lastButtonState = activeState;
pinMode(pin, (activeState) ? INPUT : INPUT_PULLUP);
}
bool tick() {
bool reading = digitalReadFast(pin);
if (reading != lastButtonState) { lastDebounceTime_ms = millis(); }
if ((millis() - lastDebounceTime_ms) > debounceDelay_ms) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == HIGH) { return true; } // Button is pressed even after the debounce time has expired
}
}
lastButtonState = reading;
return false;
}
private:
const uint8_t pin;
bool activeState;
bool buttonState {HIGH};
bool lastButtonState {LOW};
uint32_t lastDebounceTime_ms {0};
uint32_t debounceDelay_ms {50};
};
//
// Timer for non-blocking delays
//
class Timer {
public:
void start() { timeStamp = millis(); }
bool operator()(const unsigned long duration) const { return (millis() - timeStamp >= duration) ? true : false; }
private:
unsigned long timeStamp {0};
};
//
// LED Pin Data
//
struct Led {
const uint8_t pinNr;
};
//
// A dice class.
//
class Dice {
public:
template <size_t MAX> Dice(const Led (&pLeds)[MAX]) : pLeds {pLeds}, ledCount {MAX} {}
void begin() {
for (uint8_t i = 0; i < ledCount; ++i) { pinMode(pLeds[i].pinNr, OUTPUT); }
}
bool roll();
void rollReset() { rollCounter = 0; }
void lightLeds(uint8_t pips) {
for (uint8_t i = 0; i < NIBBLE; ++i) { digitalWriteFast(pLeds[i].pinNr, ((BITPATTERN[pips] >> i) & 0x01)); }
}
uint8_t getRollPips() const { return pips; }
uint8_t getBitPattern() const { return BITPATTERN[pips]; }
private:
uint8_t getRandom(uint8_t, uint8_t);
const Led *pLeds;
const uint8_t ledCount;
uint8_t pips;
Timer delay_ms;
uint8_t rollCounter {0};
uint8_t rollIterations {20};
uint16_t wait_ms;
bool isWaiting {true};
static constexpr uint8_t NIBBLE {4};
static constexpr uint8_t MIN_PIPS {1};
static constexpr uint8_t MAX_PIPS {6};
static const uint8_t BITPATTERN[7];
// static constexpr uint8_t BITPATTERN[7] {0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x0E}; // Version > C++11 necessary
};
bool Dice::roll() {
if (rollCounter > rollIterations) { rollCounter = rollIterations; }
if (isWaiting) {
if (delay_ms(wait_ms)) {
pips = random(MIN_PIPS, MAX_PIPS + 1);
lightLeds(pips);
isWaiting = false;
rollCounter++;
}
} else {
delay_ms.start();
wait_ms = random(1, 200);
isWaiting = true;
}
return (rollCounter == rollIterations) ? true : false;
}
//
// Create a random number.
//
uint8_t Dice::getRandom(uint8_t from, uint8_t to) {
uint16_t seed = analogRead(7);
seed += analogRead(1);
seed *= analogRead(2);
seed *= analogRead(3);
seed *= analogRead(4);
randomSeed(seed);
return random(from, to);
}
// Bit sequences - sequence in which the pins must be controlled
// in order to display the corresponding number of pips via LED.
// 0 7 L 6 L 5 L 4 L 0000 All off
// 1 = 7 L 6 L 5 L 4 H 0001
// 2 = 7 L 6 L 5 H 4 L 0010
// 3 = 7 L 6 L 5 H 4 H 0011
// 4 = 7 L 6 H 5 H 4 L 0110
// 5 = 7 L 6 H 5 H 4 H 0111
// 6 = 7 H 6 H 5 H 4 L 1110
// 0 1 2 3 4 5 6
const uint8_t Dice::BITPATTERN[7] {0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x0E};
////////////////////////////////////////////////////
// Global literals / variables
////////////////////////////////////////////////////
// The order with which the pin numbers are initialized in LedArray
// must be the reverse of the order of the bit sequences.
constexpr Led ledArray_d0[] {{4}, {5}, {6}, {7}}; // Pin numbers to which the LEDs are connected.
constexpr Led ledArray_d1[] {{8}, {9}, {10}, {11}}; // Pin numbers to which the LEDs are connected.
Button btn {2};
Dice dices[] {{ledArray_d0}, {ledArray_d1}};
////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////
//
// Auxiliary function for displaying bit sequences.
//
void printBits(uint8_t value) {
for (uint8_t i = 0; i < 4; ++i) { Serial.print(((value << i) & 0x08) ? 1 : 0); }
}
//
// Does the dice roll.
//
template <size_t MAX> void rollAllDices(Dice (&dice)[MAX]) {
bool isFinished {false};
while (!isFinished) {
for (uint8_t i = 0; i < MAX; i++) { isFinished = dice[i].roll(); }
}
for (uint8_t i = 0; i < MAX; i++) {
// Serial.print("Dice pips: ");
// printBits(dice[i].getBitPattern()); Serial.print(" ");
// Serial.println(dice[i].getRollPips());
dice[i].rollReset();
}
}
////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////
void setup() {
Serial.begin(115200);
btn.begin();
for (auto &dice : dices) { dice.begin(); }
// Do animation
rollAllDices(dices);
// light off leds
for (auto &dice : dices) { dice.lightLeds(0); }
}
void loop() {
if (btn.tick()) { rollAllDices(dices); }
}