ronbentley1
Published

Two x six sided die sketch using 74HC595 ICs

A sketch to randomly 'roll' 2 dice, using serial-parallel input/output ICs in a cascaded design.

IntermediateProtip506
Two x six sided die sketch using 74HC595 ICs

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
Or any Arduino compatible microcontroller
×1
Solderless Breadboard Full Size
Solderless Breadboard Full Size
×1
Solderless Breadboard Half Size
Solderless Breadboard Half Size
Or even a 1/4 size breadboard to mount the 74HC595 ICs
×1
5 mm LED: Red
5 mm LED: Red
Or use a selection of different colours
×14
Resistor 220 ohm
Resistor 220 ohm
×14
Jumper wires (generic)
Jumper wires (generic)
Lots
×1
Development Kit Accessory, Jumper Wire Kit
Development Kit Accessory, Jumper Wire Kit
Use these to keep the wiring of the LED-74HC595 connections tidy (breadboard to breadboard connections)
×1
Tactile Switch, Top Actuated
Tactile Switch, Top Actuated
Use a two terminal variety as these fit a breadboard more effectively
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Breadboard layout with 74HC595 IC and microcontroller connections

Overview of dice breadboard layout and connections to the 74HC595 ICs and to the microcontroller

74HC595 IC intra/inter connections

Overview of the 74HC595 IC mini breadboard connections in cascade/daisy chain form

Physical design, all parts

Physical breadboard layouts

Physical 74HC595 ICs, layouts

Physical microcontroller layouts

Code

A sketch to randomly throw two six sided dice, using cascaded 74HC595 serial-parallel input/output

C/C++
A sketch to randomly throw two six sided dice, using cascaded 74HC595 serial-parallel input/output ICs
// 
// Ron Bentley, Stafford, UK, December 2021
// This code is in the public domain and is offered without warranty
//
// Rolling the Dice, part 2 of 2 parts
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// A sketch to use with board games (or any game), where a two dice are required, to
// throw two six sided dice, using cascaded serial-parallel ICs, eg 74HC595 chips.
//
// Method: two electronic dice are 'thrown' to produce a random number of pips each time the
// wired button switch is pressed.
//
// By default, the sketch also drives a heart beat using the LED_BUILTIN LED, normally on pin 13,
// to show it is running (or not!).  To deselect the heart beat set the macro
// definition for heart beat on/off to false in the heart beat declaration section below, ie
// #define heart_beat_on false.
//
// Note that the circuit design calls for each die to be represented by 7 x LEDs,
// these being physically arranged to represent each of the faces/sides of a standard
// die pip layout -  
//
// Die LED pip layouts:
// The sketch uses 7 LEDs for each die to represent a partial 3 x 3 square of
// LEDs (1 x LED = 1 x pip) that allows 1 - 6 pips to be displayed in a standard die pattern, ie
//
// 1 pip  0   0, 2 pips 0   0, 3 pips X   0
//        0 X 0,        X 0 X,        0 X 0
//        0   0,        0   0,        0   X
//
// 4 pips X   X, 5 pips X   X, 6 pips X   X
//        0 0 0,        0 X 0,        X 0 X
//        X   X,        X   X,        X   X
//
// These LEDS are connected to serial/parallel I/O ICs connections QB-QH, eg 74HC595,
// in two separate sets.  Note that QA is not used in this implementation.
//
// Further reading and interest:
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// The sketch uses my ez_switch_lib and ez_SIPO8 libraries for managing the switch and SIPO ICs, respectively.
// In this instance, a button switch wired simply connected without a 10k ohm resistor (INPUT_PULLUP mode).
// See the Arduino Project Hub links below for more detailed reading:
//
// ez_switch_lib:
// https://create.arduino.cc/projecthub/ronbentley1/a-switch-library-for-arduino-dfbe40?ref=user&ref_id=1455180&offset=8
//
// ez_SIPO8_lib:
// https://create.arduino.cc/projecthub/ronbentley1/command-control-of-multiple-serial-in-parallel-out-ics-3e0b1a?ref=user&ref_id=1455180&offset=7
//
// heart_beat monitoring:
//
// https://create.arduino.cc/projecthub/ronbentley1/implementing-a-heart-beat-in-your-code-3f3460?ref=user&ref_id=1455180&offset=5
//

#include <ez_switch_lib.h>
#include <ez_SIPO8_lib.h>

//
// Define switch data required for the sketch...
//
#define button_switch_pin  12 // digital pin assigned to the switch
int switch_id;                // stores the switch 'token' for the switch following the add_switch call

//
// Define serial/parallel I/O (SIPO) data required for the sketch...
//
#define data_pin   2
#define latch_pin  3
#define clock_pin  4
#define num_SIPOs  2      // 2 x 8 bit SIPO ICs linked in cascade formation
#define num_SIPO_timers 0 // no SIPO timers used in this example
int bank_id;              // holds the SIPO bank ID given by the create_bank function

//
// Define heart beat data...
//
#define heart_beat_pin   LED_BUILTIN  // digital pin for heart beat LED
#define heart_beat_on    true         // determines if the implementation uses the heartbeat
long unsigned heart_beat_freq = 1000; // time(milliseconds) of heart beat frequency
long unsigned heart_beat_on_off_time; // the time the LED is on and off - 1/2 frequency
long unsigned last_heart_beat_time;   // time in milliseconds of last heart beat status change
bool heart_beat_status = HIGH;        // current status of heart beat, start high

//
// Function handles the heart beat cycle.
// May be called from anywhere, but at least every main loop cycle.
//
void heart_beat() {
  if (heart_beat_on) {
    if (millis() - last_heart_beat_time >= heart_beat_on_off_time) {
      // time to swap status of the heart beat LED and update it
      last_heart_beat_time = millis();
      heart_beat_status = !heart_beat_status;           // invert current heart beat status value
      digitalWrite(heart_beat_pin, heart_beat_status);  // update LED with new status
    }
  }
}

//
// This array is used to define the SIPO outputs that connect to the LEDs
// that make up each of the die face patterns/layouts.
// Each element of the array (faces) corresonds to the binary pattern of the
// pip value thrown (0-5) for each face/side of a die.
// The SIPO outputs are referenced as QA - QH, with the LSB (0) being QA, etc.
// See SIPO documentations.
// Note that QA is not used in this implementation.
//
#define num_dice           num_SIPOs // because we are using one SIPO IC for each die
#define faces_per_die      6
#define die_1              0
#define die_2              1
uint8_t pip_patterns[faces_per_die] = {
  // Die pip patterns, faces 1-6 (array index 0-5) across 7 SIPO connected leds
  0b00010000, // 1 pip,  just the central LED connected to SIPO output         QE
  0b01000100, // 2 pips, each central two outer LEDs connected to SIPO outputs QC and QG
  0b00111000, // 3 pips, diagonal LEDs connected to SIPO outputs               QD, QE and QF
  0b10101010, // 4 pips, each corner LED connected to SIPO outputs             QB, QD, Q5 and QH
  0b10111010, // 5 pips, all LEDs connected to SIPO outputs                    QB, QD, QE, Q5 and QH
  0b11101110  // 6 pips, each outer column of 3 LEDs connected to SIPO outputs QB, QC, QD, QF, QG and QH
};

// Create the switch control instance...
Switches my_switches(1); // define a switch control structure for a single switch

// Create the SIPO control instance for the number of dice defined...
SIPO8 my_SIPOs(num_dice, num_SIPO_timers); // define a SIPO control structure for the number of dice (SIPO ICs) required

//
// Clear down the pips/LEDs
//
void clear_pips() {
  my_SIPOs.set_bank(bank_id, LOW);       // set all bits in this bank to 0 (off)
  my_SIPOs.xfer_bank(bank_id, MSBFIRST); // write out the SIPO bank data to the SIPO ICs
}

//
// Announces (signals) that a dice throw is about to start
//
void announce_throw() {
#define num_cycles    2
#define num_patterns  5
  uint8_t announce_pattern[num_patterns] = {// simple strobe sequence - down and up
    0b00100010, // outer two top LEDs of a die
    0b01000100, // outer two middle LEDs of a die
    0b10001000, // outer two bottom LEDs of a die
    0b01000100, // outer two middle LEDs of a die
    0b00100010  // outer two top LEDs of a die
  };
  // Start by clearing down the existing die pips/score
  clear_pips();
  for (uint8_t cycle = 1; cycle <= num_cycles; cycle++) {// do 'num_cycles' cycles
    // send each face/side strobe pattern to each die with a short delay between each
    for (uint8_t pattern = 0; pattern < num_patterns; pattern++) {
      my_SIPOs.set_bank_SIPO(bank_id, die_1, announce_pattern[pattern]); // set first die to this 'pattern'
      my_SIPOs.set_bank_SIPO(bank_id, die_2, announce_pattern[pattern]); // set second die to this 'pattern'
      my_SIPOs.xfer_bank(bank_id, MSBFIRST); // transfer (xfer) out the dice bits to the physical cascaded SIPOs
      delay(50);
      heart_beat(); // keep pumping the heart beat
      clear_pips();
    }
  }
}

//
// Throw the die
//
void throw_dice() {
  announce_throw(); // 'announce' the throw of the die
  randomSeed(analogRead(A0) * 31 +
             analogRead(A1) * 37 +
             random(1023, 10000));                // keep changing the seed
  uint8_t die_face;
  die_face = random(1, 104640) % faces_per_die;   // range 0-(faces_per_die-1)
  my_SIPOs.set_bank_SIPO(bank_id,
                         die_1, // first die in the bank
                         pip_patterns[die_face]); // load first die pip patern for this face
  die_face = random(1, 104640) % faces_per_die;   // range 0-(faces_per_die-1)
  my_SIPOs.set_bank_SIPO(bank_id,
                         die_2, // second die in the bank
                         pip_patterns[die_face]); // load second die pip pattern for this face
  my_SIPOs.xfer_bank(bank_id, MSBFIRST);          // move dice data to the physical cascaded SIPOs
}

void setup() {
  // **** Create a switch - a button switch wired without
  // **** a 10k ohm resistor (circuit_C2 - INPUT_PULLUP)
  switch_id = my_switches.add_switch(button_switch, button_switch_pin, circuit_C2);
  if (switch_id < 0) {
    // failure to add a switch!
    exit(0);
  }
  // **** Create the SIPO bank of size
  // **** = number of individual SIPO ICs (num_SIPOs) in the cascaded bank
  bank_id = my_SIPOs.create_bank(data_pin, clock_pin, latch_pin, num_SIPOs);
  if (bank_id == create_bank_failure ) {
    // failure to create a SIPO bank
    exit(0);
  }
  if (heart_beat_on) {
    // **** Setup heart beat, if selected
    pinMode(heart_beat_pin, OUTPUT);
    last_heart_beat_time   = millis();            // start of heartbeat timer
    heart_beat_on_off_time = heart_beat_freq / 2; // LED is on and off at 1/2 frequency time
    // end of heart beat setup
  }
  announce_throw(); // illuminate the LEDs on startup
}

void loop() {
  do {
    heart_beat(); // keep pumping the heart beat timer every cycle
    if (my_switches.read_switch(switch_id) == switched) {
      // Switch has been pressed and released, so throw the die...
      throw_dice();
    }
  } while (true);
}

Credits

ronbentley1
25 projects • 13 followers

Comments