Close
0%
0%

Dice badge

Put a bit of colorful randomness on your clothes !

Similar projects worth following
It's been a long time I wanted to design a pcb which would be decorative, or wearable. But it had to be useful too. So after seeing a dice here on hackaday (I believe it was made with 555 timers, but I cannot find it), I thought a dice badge would be nice.

It's based on an ATTiny 85, it has seven leds and their resistances, a push button, a cell and a pin, all packed in a 26mm square. The CR1225 cell can last quite long ( the ones I wear are almost one year old), thanks to the ATTiny deep sleep modes.

I've made them red, violet and orange (JLCPCB's yellow is orangeish), with assorted leds. Blue and green are coming too.

There is a short video here : https://www.instagram.com/p/CcAT7ykI-jj

And they should be on Tindie soon, I've opened an account just for them !

Note : sorry for the pictures borrowed from Instagram, I've lost the originals with the death of a HDD...

The badge have six different modes, which are :

  • Throw a dice : each push on the button will make the dice blink several random number, then stop and display for a few seconds.
  • Heartbeat, normal : each tree seconds, the leds blink twice, fast then slow, like a heart beat.
  • Hearbeat, slow : same, but with a six seconds period.
  • Pulse, normal : it simply blinks every three seconds.
  • Pulse, slow : same, but with a six seconds period.
  • Random : continuous display of random numbers with random fade in and out durations.

Each mode uses fade in and fade out on leds. Mode is changed by long-pressing (more than one second) the button.

Whenever the leds are not lit, the system goes to deep sleep to preserve the (small) cell. More about that in the logs.

Dice_badge.pdf

Schematics.

Adobe Portable Document Format - 111.75 kB - 02/22/2023 at 22:55

Preview
Download

  • 1 × ATTiny 85 small microcontroller
  • 1 × push button It's a panasonic EVP-BFAC1A000,because they have a nice feeling
  • 7 × Leds, in 0805 format. Matching the PCB color.
  • 2 × Resistance array In 4x0603 format, to save room. 68Ohms
  • 1 × capacitor for decoupling This one is a 0603, 100nF.

View all 8 components

  • About adding complexity to simplify the code.

    Pierre-Loup M.02/23/2023 at 08:31 0 comments

    When I was coding the program for the badge, I started with simple things : led on, or led off. Then I added fade in and out. Then I tought a heartbeat pulsation (fast pulse, then a slower) would be nice. I was fast having the almost same chunk of code in several places, with just the delays changing.

    And there was the case where the system is put to sleep. It's nicer if the leds takes their times to fade out before sleeping.

    I ended up implementing a queue for all that. That may be a bit overkilled for that, but that simplifies the global handling.

    struct wait_t{
    	uint16_t delay;
    	uint32_t start;
    };
    
    struct fade_t{
    	int8_t direction;
    	volatile uint16_t duration;
    	uint8_t limit;
    	bool comp;
    };
    
    struct sleep_t{
    	int16_t delay;
    	bool shortSleep;
    };
    
    struct state_t{
    	state_t *next;
    	volatile uint8_t state;
    	union{
    		fade_t fade;
    		wait_t wait;
    		sleep_t sleep;
    		uint8_t value;
    	};
    };

     The state_t struct contains three things : the type of operation it represents, the parameters linked to this operation, and a pointer to the next operation. These operations can be of four type :

    • wait : do nothing for a time, typically when the leds are lit. Stores the delay, and the value from millis() when the wait starts.
    • fade : fade in, or fade out. Stores the direction (in or out), the duration of the fade and the limit (we can make a fade from 10 to 35% duty cycle, for example).
    • sleep : program a sleep. Stores the kind of sleep : if we are between to blinks, or if we go foe a deep, long sleep.
    • change number : change the number displayed by the leds. This one doesn't have its struct,  it's directly written to the state_t struct.
    • There is a fifth state, which is Idle : nothing happens.

    Each of this action has its own function in the code, which are called by a dispatcher. The Union in the state_t makes it easy to handle several kind of informations within the same structure.

    It makes complex blink patterns easier to set. For example the dice throw (mode 1) is as follow :

    void throwDice(){
    	uint8_t limit = xorshift(4) + 3;
    	uint8_t speed = 4;
    
    	for(uint8_t i = 0; i < limit; ++i){
    		queueFadeOut(speed);
    		queueNumber(xorshift(6) + 1);
    		queueFadeIn(speed);
    		speed += xorshift(4);
    	}
    	queueDelay(5000);
    	queueFadeOut(FADE_SLOW);
    	queueSleep();
    }

     It makes the leds blink between 4 and 7 times a random number with fade in and out, then display the last one for 5 seconds, then go to long sleep. Once this function has been called to queue all the steps at once, main loop calls the dispatcher that handles each step when needed.

  • About keeping your badge running for a long time !

    Pierre-Loup M.02/23/2023 at 07:59 0 comments

    One of the challenge with wearable is you have to make the system last as long as possible on a battery. Most microcontrollers have different levels of awakeness (or sleepness, as you want), that fullfill different needs, with different current consumption.

    The ATTiny 25/45/85, being a simple (and a bit old) microcontorller, has only three :

    • Active : not really a sleep mode, it's when it's awake. Around 5mA (plus current from each pin)
    • Idle : the CPU is stopped,but most of the peripherals still work. Around 1.2mA
    • ADC noise reduction : some more things shutdown.
    • Power-down : in this mode every clocks and peripherals are stopped, only the watchdog, external interrupts and reset are still enables. max 10uA of current consumption with watchdog, 2uA without.

    You can enable sleep by manipulating the registers to set the options you need. Some functions are also available by including "avr/sleep.h" in you program. The documentation is well made, and there are a lot of tutorials about that online.

    On the badge, deep sleep (power-down) is implemented as soon as the leds are stopped. There are two cases :

    • Mode 1, dice : once the leds have been lit for a few seconds, the program goes to sleep until the button is pressed again. Before going to sleep the timer that handles the PWM is stopped and the interrupt is enabled so a button push can wake the program from sleep.
    • All other modes : the sleep is a pause between two blinks, so the settings are the same as above, but we also set the watchdog timer to wake up the program every 125ms, to see if enough time as passed.

    On wake up, the timer for led dimming is started again, the interrupt on the button is removed, and its state is forced to 0, otherwise after waking up, the program would account for that press and send back the system to sleep. The watchdog timer is also stopped.

    On mode 1 (Dice), the battery last very long, several month by awaking the badge for a dice throw every now and then.
    On other modes, the leds are lit almost all the time, battery only holds for about three hours.

    One thing that could be made to lengthen the battery life would be to limit the duty cycle of the leds to 50%. The decrease in intensity would be light to the eye, but that would half the current consumption.

  • About randomness

    Pierre-Loup M.02/22/2023 at 22:39 0 comments

    Here is were this project becomes a bit snob : randomness. I wanted the randomness to be as random as possible. And of course we all know that is almost impossible without a true random number generator (RNG). With the Arduino ecosystem we only have pseudo-randomness. But we can try to improve that a bit. A few years ago I read an article about achieving randomness on microcontrollers. You may have read it too, it's here. I invite you to read the article if you have not (or to read it again !), but here is the pitch : on a microcontroller, the flip-flop composing each bits of the RAM will init in a given state. On most bits it will always be the same state (i.e. 0 or 1), but on some this initial state is undefined. This uncertainty is used to seed the random number generator : on startup each and every byte of the memory is read, shifted as needed and xor'ed with the previous read. Once the memory has been read, this number is used as the seed.

    The other part of randomness is the algorithm used. After reading a few articles on wikipedia (and understanding very few of them) I implemented an XORshift algorithm for generating random numbers. Arduino's random was probably good enough, but that's a bit of accomplishment to add to the project. :)

    Xorshift is quite good and only needs four lines of code. You can read about it on wikipedia.

  • About leds

    Pierre-Loup M.02/22/2023 at 22:25 0 comments

    My first thought was "we need seven dots if we want to display the numbers from 1 to 6 on a dice". When you wanna use a microcontroller that has only six pins available, and one being the reset (so that's five), you have to find a solution. I was ready to dive into charlyplexing, but I quickly realized that six of these dots were always in pair : we in fact need just four pins. one for the central dot for odd numbers, and three for the pairs used for even numbers. See the schematics on the previous log to see how it's routed.

    I also wanted to have a smooth on and off dimming. That implies PWM, which is easy to do on a microcontroller when you have one or two leds. Usually you tie the led to an output driven by the uC. but the ATTiny only has three of its pins that can be driven in such a way. After a bit of thinking, I implemented a quite simple PWM for all leds at once. A timer is used with two of its interrupts :

    • On overflow (loop back to 0) all the leds are set, according to their state (i.e. for a three, only a diagonal is set and will lit). The change of duty cycle is also handled here, but I'll come back to it in a later log.
    • On compare match (duty cycle) all the leds are shut.

    It's probably a bit less efficient than driving the leds directly from the PWM pins, but all the leds are dimmable. The only drawback is they are not independent. That's not a big deal in that case.

    Here is the code of the interrupts. As you can see it's straightforward :

    ISR(TIMER1_OVF_vect){
    	// Clearing all four leds)
    	PORTB &= ~(0b1111);
    	// overflow counter
    	if(++overflowCounter >= queueOut->fade.duration){
    		overflowCounter = 0;
    		overflow = true;
    	}
    }
    
    ISR(TIMER1_COMPA_vect){
    	// Set all four leds to their predefined values
    	if(OCR1A != 255) PORTB |= pinState;
    } 

  • About PCB

    Pierre-Loup M.02/22/2023 at 21:59 0 comments

    A few details about the hardware. The PCB has been designed with kicad. The circuit is quite simple, the main difficulty is to have everything fit on the small PCB :

    • ATTiny 85
    • program port (smd pad version)
    • 7 leds
    • resistances (resistance arrays were used to save surface)
    • decoupling capacitor
    • a push button
    • a cell
    • a pin, we want to wear it !

    Of course being simple doesn't mean the first one had no problem. In fact it had several issues :

    • A track was forgotten, so some enameled wire and hand soldering were used to correct it. Namely, the ground connection of the push button was not linked to the ground of the whole circuit.
    • The pin was soldered on a grounded pad, meaning that it could short the cell (and it indeed did !)
    • The cell was a CR1015, too small to last long.
    • The black color was too sad for the badge.
    • Some subtle changes were also made to the outline and the hatch pattern.

    The second batch was perfectly fine, with no errors ! :)

    Here is the schematics for the badge :

    Here is the circuit (viewed from front) :

    And here are the kicad renderings :

    Given the very small size of the PCB, and the fact that most PCB manufacturer bill the same up to 10cm, I've panelized them using Kikit (a tool you might try if you don't know it yet : see it on github).

    It's a matter of minutes to have the boards appended to a project, you just run a command again if you have any change to do, it's clean and easy. And it even create the files with the formatting your fabhouse wants. :)

    Here is the panelized board, with framing, v-cuts, holes for centering the stencil, and room for JLCPCB to put their internal fab number (you guessed it !) :

View all 5 project logs

Enjoy this project?

Share

Discussions

Myroslav Galavai wrote 04/12/2023 at 07:19 point

I'd buy a couple for my daughter - avid ad&d player.

  Are you sure? yes | no

Ulte wrote 03/01/2023 at 17:57 point

If it gets on tindie I'll def buy a few

  Are you sure? yes | no

Pierre-Loup M. wrote 03/01/2023 at 17:58 point

One good reason to speed up things! :)
Thank you.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates