Electronic dice in keychain format

This page is dedicated to my newest project, an electronic dice which comes in a keychain format. It is supplied by a single CR2032 button battery, which provides power for a long time. This is since the battery is connected to the circuit by the push button, so when the push button is not pressed there is no power.

electronic dice keychain
Electronic dice keychain

Measuring 21x37mm and having a keyring attached to the PCB, it is easily carried around wil you. It features just a few components, as we will sell below.

The idea

I actually like and enjoy making small PCBs, designing circuits that fit in the smallest possible spaces. I have recently designed very small PCBs for an egg timer, a FritzenLab keychain, a wedding keychain and now this electronic dice. Then one of these days I stumbled upon the idea below, which is exactly a dice keychain:

FKW Designs

The actual link for the product I got inspiration from is here, have a look. I then opened my Kicad software and started designing; it was a pretty quick process, I used charlieplexing (which I had already used here) to accomodate seven LEDs in four Attiny85 pins.

The hardware

As stated above, I used an Attiny85 as the brains of the project, along with seven LEDs, four resistors, one SMD push button, 2032 battery holder and 2032 battery (non-rechargeable). Schematic is seen below and also in my Github.

Dice keychain schematic diagram
Dice keychain schematic diagram

I was able to use a single CR2032 battery to supply the Attiny85 because its dataseet states suppling voltage of 1.8V to 5.5V for 10MHz. CR2032 batteries have a nominal voltage of 3V and go down to 2V, according to their datasheet. Capacitor C1 is a 100nF just for power cleanliness for the Attiny85. I put J1 as a programming connector for the Attiny85, using Arduino UNO as ISP as seen below.

How to program the dice keychain, Arduino as ISP
How to program the dice keychain, Arduino as ISP

Also as stated above this battery will potentially last a long time, since the push button completely disconnects power from the circuit when it is not pressed. Also I selected those four LED resistors to be 330R since at any time there will be two resistors in series with any LED. I had initially selected 1.5k Ohm for each, but the LED were too dim.

Regarding layout there is nothing really special to it, I made a ground plane just in case. But there is no high frequency involved or anything, so not really necessary. The only intentionally noise part is the big huge trace connected to analog A2 (pin 3), since this analog input is used as source of randomness for the random() function.

Dice keychain Kicad layout
Dice keychain Kicad layout

Code/software

Despite getting the product idea from the link mentioned above, I did not even look at the code for it. One of the things about myself is that I like to code everything myself, I do not work well with code from other people. Also because if I do myself I will learn, otherwise not.

So the code for this electronic dice is mine, from ground up. I have created two IFs, one that is entered every 1ms (one milissecond) for the LEDs and another every two seconds, with a matrix that defines which LEDs will light up. This second IF (2 seconds) is only entered once, meaning the sorted number is only one and will remain the same as long as you keep pressing the button.

So one IF defines the number to be shown (only one per power up) and the other IF sweeps through all seven LEDs deciding which one will light up. Pretty organized and simple code, if you ask me. You can see it below and also on my Github.

int bitsresultados[7];
int passagem= 0;

// Variables used on this code
unsigned long time1;
unsigned long previousTime;
boolean enterFunction = true;
unsigned long time2;
unsigned long previousTime2;
boolean enterFunction2 = true;
//-----------------------

int digitoaentrar = 1;
bool sortedanumber= 0;
uint16_t sortnumber= 0;
int entered = 0;

void setup() {
  
  //Serial.begin(9600);
  
}

void loop() {

  time1 = micros();
  time2 = micros();
  if (enterFunction == true) {
    previousTime = time1;
    passagem++;
    // Start your code below
    //-----------------------

    
    if (digitoaentrar == 1 && passagem == 1) {
      if (bitsresultados[6] == 1) {
        pinMode(3, OUTPUT);
        pinMode(0, OUTPUT);
        pinMode(1, INPUT);
        pinMode(2, INPUT);
        digitalWrite(3, HIGH);
        digitalWrite(0, LOW);
      }
      digitoaentrar = 2;
    }
    if (digitoaentrar == 2 && passagem == 2) {
      if (bitsresultados[5] == 1) {
        pinMode(3, OUTPUT);
        pinMode(0, OUTPUT);
        pinMode(1, INPUT);
        pinMode(2, INPUT);
        digitalWrite(3, LOW);
        digitalWrite(0, HIGH);
      }
      digitoaentrar = 3;
    }
    if (digitoaentrar == 3 && passagem == 3) {
      if (bitsresultados[4] == 1) {
        pinMode(3, INPUT);
        pinMode(0, OUTPUT);
        pinMode(1, OUTPUT);
        pinMode(2, INPUT);
        digitalWrite(0, HIGH);
        digitalWrite(1, LOW);
      }
      digitoaentrar = 4;
    }
    if (digitoaentrar == 4 && passagem == 4) {
      if (bitsresultados[3] == 1) {
        pinMode(3, INPUT);
        pinMode(0, OUTPUT);
        pinMode(1, OUTPUT);
        pinMode(2, INPUT);
        digitalWrite(0, LOW);
        digitalWrite(1, HIGH);
      }
      digitoaentrar = 5;
    }
    if (digitoaentrar == 5 && passagem == 5) {
      if (bitsresultados[2] == 1) {
        pinMode(3, OUTPUT);
        pinMode(0, INPUT);
        pinMode(1, OUTPUT);
        pinMode(2, INPUT);
        digitalWrite(3, HIGH);
        digitalWrite(1, LOW);
      }
      digitoaentrar = 6;
    }
    if (digitoaentrar == 6 && passagem == 6) {
      if (bitsresultados[1] == 1) {
        pinMode(3, OUTPUT);
        pinMode(0, INPUT);
        pinMode(1, OUTPUT);
        pinMode(2, INPUT);
        digitalWrite(3, LOW);
        digitalWrite(1, HIGH);
      }
      digitoaentrar= 7;
      
    }
    if (digitoaentrar == 7 && passagem == 7) {
      if (bitsresultados[0] == 1) {
        pinMode(3, INPUT);
        pinMode(0, INPUT);
        pinMode(1, OUTPUT);
        pinMode(2, OUTPUT);
        digitalWrite(2, LOW);
        digitalWrite(1, HIGH);
      }
      digitoaentrar= 1;
      passagem= 0;
    }

    //-----------------------
    // End of your code
  }

  if (enterFunction2 == true && entered == 0) {
    previousTime2 = time2;

    if(sortedanumber == 0){
      randomSeed(analogRead(A2));
      sortnumber = random(1, 7);
      sortedanumber = 1;
    }
    
    if(sortnumber == 1){
      bitsresultados[0] = 0;
      bitsresultados[1] = 0;
      bitsresultados[2] = 0;
      bitsresultados[3] = 1;
      bitsresultados[4] = 0;
      bitsresultados[5] = 0;
      bitsresultados[6] = 0;

    }else if(sortnumber == 2){
      bitsresultados[0] = 0;
      bitsresultados[1] = 0;
      bitsresultados[2] = 1;
      bitsresultados[3] = 0;
      bitsresultados[4] = 0;
      bitsresultados[5] = 0;
      bitsresultados[6] = 1;
    }else if(sortnumber == 3){
      bitsresultados[0] = 0;
      bitsresultados[1] = 0;
      bitsresultados[2] = 1;
      bitsresultados[3] = 1;
      bitsresultados[4] = 0;
      bitsresultados[5] = 0;
      bitsresultados[6] = 1;
    }else if(sortnumber == 4){
      bitsresultados[0] = 1;
      bitsresultados[1] = 0;
      bitsresultados[2] = 1;
      bitsresultados[3] = 0;
      bitsresultados[4] = 1;
      bitsresultados[5] = 0;
      bitsresultados[6] = 1;
    }else if(sortnumber == 5){
      bitsresultados[0] = 1;
      bitsresultados[1] = 0;
      bitsresultados[2] = 1;
      bitsresultados[3] = 1;
      bitsresultados[4] = 1;
      bitsresultados[5] = 0;
      bitsresultados[6] = 1;
    }else if(sortnumber == 6){
      bitsresultados[0] = 1;
      bitsresultados[1] = 1;
      bitsresultados[2] = 1;
      bitsresultados[3] = 0;
      bitsresultados[4] = 1;
      bitsresultados[5] = 1;
      bitsresultados[6] = 1;
    }else{

    }
    entered = 1;
      
    
    

    
      
    
    
  }


  // The DELAY time is adjusted in the constant below >>
  if (time1 - previousTime < 990) { // 1 million microsencods= 1 second delay
    /* I have actually used 0.999990 seconds, in a trial to compensate the time that
       this IF function takes to be executed. this is really a point that
       need improvement in my code */
    enterFunction = false;
  }
  else {
    enterFunction = true;
  }
  if (time2 - previousTime2 < 1999990) { // 1 million microsencods= 1 second delay
    /* I have actually used 0.999990 seconds, in a trial to compensate the time that
       this IF function takes to be executed. this is really a point that
       need improvement in my code */
    enterFunction2 = false;
  }
  else {
    enterFunction2 = true;
  }

}

End result

I made a quick video showing how this dice keychain works, enjoy: