Health Bar for Magic: The Gathering
Obliczymy ostateczną cenę na podstawie wybranego pliku. Sprawdzisz ją i zaakceptujesz przed płatnością.
Zapisane na tym urządzeniu.
It's an electronic health-bar life point counter that I use for playing Magic, definitely cooler than ur average D20
- Ocena
- 0 / 5
- Polubienia
- 13
- Pobrania
- 15
- Wydruki
- 0
- Wyświetlenia
- 1229
- Oceny
- 0
Aktualizacja u źródła:
Informacje o modelu
- Źródło
- Printables
- Kategoria
- Other Toys & Games
- Licencja
- Creative Commons — Attribution — Noncommercial
See here for a video on how it looks like when it's working:
https://www.instagram.com/p/DC7ZvZqqY5L/?igsh=ZG85azRucWNyZ2Ry
Also, DM me there for questions.
Description:
I made a health bar style life counter for MtG using an Adafruit Trinket M0 (any controller would do tho, I just don't think any other would fit inside my case).
Parts and assembly:
I've used 2x20 Adafruit NeoPixel LED strips (144/m because it's a pretty tight design).
You will also need a rotary encoder (I've used a 318-ENC130175F-12PS, the data sheet says REF. TW-700198 as well, image attached).
Just solder the data pin of the two strips to pins 3 and 4 of the Trinket M0.
The CLK and DT pins (the left and right of the 3 upfront) of the rotary encoder need to go to pins 0 and 2 respectively, while the SW pin (the left one of the two on the back for the button click) should go to pin 1.
The other two pins would need to go to ground, since I've coded pull-up resistors on all of them.
I've used a 5V usb cable (any power bank or usb to power socket on the other end will do) and soldered it directly to the breadboard of the Trinket M0.
I'd recommend soldering the trinket on one side and the rotary encoder on the other.
I've used a small Adafruit half breadboard, it pretty much exactly fits inside the dimensions of the body.
To close the thing, I've used 3 M3 countersunk screws (16mm long) and 3 M3 nuts, which you can push inside the case on each side.
The .stls labelled “small” are optional, I've used these to fill in the marks on top of the case, but you can also just color these in if you like.
I've printed everything at 0.2mm on a Prusa MK4 with Prusament PETG.
How does it work:
You just spin the rotary encoder to increase / decrease each players life total.
If you click the rotary button, you switch the player (i.e. which LED strip is affected).
Here you can find my code:
#include <Adafruit_NeoPixel.h>// Rotary Encoder Inputs
#define CLK 0
#define DT 2
#define SW 1
#define LED_PIN_1 3
#define LED_PIN_2 4
bool whichPlayer = false;
int lifeTotalOne = 20;
int lifeTotalTwo = 20;
int counter = 0;
int currentStateCLK;
int lastStateCLK;
uint32_t curColors[20];
String currentDir ="";
Adafruit_NeoPixel strip1(20, LED_PIN_1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2(20, LED_PIN_2, NEO_GRB + NEO_KHZ800);
unsigned long lastButtonPress = 0;
//base color array
#include <cstdint>
uint32_t base_colors[20] = {
0xB20000, // Strong Red
0xB21300,
0xB22600,
0xB24700,
0xB25900,
0xB26B00,
0xB27E00,
0xB29000,
0xB2A300, // Yellowish Red
0xB2B300, // Yellow
0x99B300, // Greenish Yellow
0x8FB300,
0x7AB300,
0x6EB300,
0x59B300,
0x4CB300,
0x3FB300,
0x33B300,
0x26B300,
0x00B200 // Strong Green
};
uint32_t green_to_blue[20] = {
0x00B200, // Strong Green
0x00B213,
0x00B226,
0x00A313, // Midpoint: Blue-Green
0x00B23F,
0x00B259,
0x00A326,
0x008F3F,
0x007359,
0x00B26B,
0x00B27E,
0x00B290,
0x00B2A3,
0x00B2B5, // Light Green
0x005959, // Transition to blue-green
0x004C66,
0x003373,
0x002680,
0x001399,
0x0000B2 // Strong Blue
};
uint32_t blue_to_purple[20] = {
0x0000B2,
0x0500B2,
0x0A00B2,
0x1000B2,
0x1400B2,
0x1900B2,
0x1E00B2,
0x2300B2,
0x2800B2,
0x2D00B2,
0x3200B2,
0x3800B2,
0x3C00B2,
0x4100B2,
0x4600B2,
0x4B00B2,
0x5000B2,
0x5500B2,
0x5A00B2,
0x6100B2 // Light Violet
};
uint32_t purple_to_orange[20] = {
0x6100B2, // Deep Violet
0x6C00A9, // Violet
0x76009F, // Violet-Purple
0x860096, // Purple
0x93008C, // Purple-Pink
0x9E0083, // Pinkish Purple
0xA60078, // Pink-Magenta
0xB2006B, // Magenta
0xB21366, // Magenta-Red
0xB23359, // Red-Orange
0xB24C4D, // Bright Red-Orange
0xB25940, // Rich Orange-Red
0xB26B33, // Deep Orange
0xB26B33, // Vibrant Orange
0xB27E33, // Bright Orange
0xB2A33F, // Yellowish Orange
0xB2B23F, // Orange-Yellow
0xB2B539, // Warm Yellow-Orange
0xB2B239, // Bright Warm Yellow
0xB26F38 // Strong Orange
};
void setup() {
// Set encoder pins as inputs
pinMode(CLK,INPUT_PULLUP);
pinMode(DT,INPUT_PULLUP);
pinMode(SW, INPUT_PULLUP);
// Setup Serial Monitor
Serial.begin(9600);
// Read the initial state of CLK
lastStateCLK = digitalRead(CLK);
strip1.begin();
strip1.show(); // Initialize all pixels to 'off'
strip2.begin();
strip2.show(); // Initialize all pixels to 'off'
//strip1.setBrightness(200);
//strip2.setBrightness(200);
updateStrips();
}
void loop() {
// Read the current state of CLK
currentStateCLK = digitalRead(CLK);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
// If the DT state is different than the CLK state then
// the encoder is rotating CCW so decrement
if (digitalRead(DT) != currentStateCLK) {
counter --;
currentDir ="CCW";
if(!whichPlayer){
lifeTotalOne--;
}else{
lifeTotalTwo--;
}
} else {
// Encoder is rotating CW so increment
counter ++;
currentDir ="CW";
if(!whichPlayer){
lifeTotalOne++;
}else{
lifeTotalTwo++;
}
}
updateStrips();
//Serial.print("Direction: ");
//Serial.print(currentDir);
//Serial.print(" | Counter: ");
//Serial.println(counter);
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
// Read the button state
int btnState = digitalRead(SW);
//If we detect LOW signal, button is pressed
if (btnState == LOW) {
//if 50ms have passed since last LOW pulse, it means that the
//button has been pressed, released and pressed again
if (millis() - lastButtonPress > 50) {
//Serial.println("Button pressed!");
if(whichPlayer){
whichPlayer = false;
}else{
whichPlayer = true;
}
indicatePlayer();
}
// Remember last button press event
lastButtonPress = millis();
}
// Put in a slight delay to help debounce the reading
delay(1);
}
void indicatePlayer(){
if(whichPlayer){
for(int i = 0; i < 20; i++){
curColors[i] = strip2.getPixelColor(i);
}
for(int i = 0; i < 10; i++){
strip2.setPixelColor(19-i, 0x00DDAA);
strip2.setPixelColor(i, 0x00DDAA);
strip2.show();
delay(20);
}
for(int i = 0; i < 10; i++){
strip2.setPixelColor(19-i, curColors[19-i]);
strip2.setPixelColor(i, curColors[i]);
strip2.show();
delay(20);
}
}else{
for(int i = 0; i < 20; i++){
curColors[i] = strip1.getPixelColor(i);
}
for(int i = 0; i < 10; i++){
strip1.setPixelColor(19-i, 0x00DDAA);
strip1.setPixelColor(i, 0x00DDAA);
strip1.show();
delay(20);
}
for(int i = 0; i < 10; i++){
strip1.setPixelColor(19-i, curColors[19-i]);
strip1.setPixelColor(i, curColors[i]);
strip1.show();
delay(20);
}
}
}
void updateStrips(){
// below 20
if(lifeTotalOne <=20){
for(int i = 0; i < 20; i++){
strip1.setPixelColor(i, 0x000000);
}
for(int i = 0; i < lifeTotalOne; i++){
strip1.setPixelColor(i, base_colors[i]);
}
Serial.print("<=20 | LifeTotalOne: ");
Serial.println(lifeTotalOne);
}
strip1.show();
if(lifeTotalTwo <=20){
for(int i = 0; i < 20; i++){
strip2.setPixelColor(i, 0x000000);
}
for(int i = 0; i < lifeTotalTwo; i++){
strip2.setPixelColor(i, base_colors[i]);
}
}
strip2.show();
//between 20 and 40
if(lifeTotalOne >20 && lifeTotalOne <=40 ){
for(int i = 0; i < 20; i++){
strip1.setPixelColor(i, 0x000000);
}
if((lifeTotalOne%20)==0){
for(int i = 0; i < 20; i++){
strip1.setPixelColor(i, green_to_blue[i]);
}
}else{
for(int i = 0; i < (lifeTotalOne%20); i++){
strip1.setPixelColor(i, green_to_blue[i]);
}
}
}
strip1.show();
if(lifeTotalTwo >20 && lifeTotalTwo <=40 ){
for(int i = 0; i < 20; i++){
strip2.setPixelColor(i, 0x000000);
}
if((lifeTotalTwo%20)==0){
for(int i = 0; i < 20; i++){
strip2.setPixelColor(i, green_to_blue[i]);
}
}else{
for(int i = 0; i < (lifeTotalTwo%20); i++){
strip2.setPixelColor(i, green_to_blue[i]);
}
}
}
strip2.show();
//between 40 and 60
if(lifeTotalOne >40 && lifeTotalOne <=60 ){
for(int i = 0; i < 20; i++){
strip1.setPixelColor(i, 0x000000);
}
if((lifeTotalOne%20)==0){
for(int i = 0; i < 20; i++){
strip1.setPixelColor(i, blue_to_purple[i]);
}
}else{
for(int i = 0; i < (lifeTotalOne%20); i++){
strip1.setPixelColor(i, blue_to_purple[i]);
}
}
Serial.print(">20 | LifeTotalOne: ");
Serial.println(lifeTotalOne);
}
strip1.show();
if(lifeTotalTwo >40 && lifeTotalTwo <=60 ){
for(int i = 0; i < 20; i++){
strip2.setPixelColor(i, 0x000000);
}
if((lifeTotalTwo%20)==0){
for(int i = 0; i < 20; i++){
strip2.setPixelColor(i, blue_to_purple[i]);
}
}else{
for(int i = 0; i < (lifeTotalTwo%20); i++){
strip2.setPixelColor(i, blue_to_purple[i]);
}
}
}
strip2.show();
// between 60 and 80
if(lifeTotalOne >60 && lifeTotalOne <=80 ){
for(int i = 0; i < 20; i++){
strip1.setPixelColor(i, 0x000000);
}
if((lifeTotalOne%20)==0){
for(int i = 0; i < 20; i++){
strip1.setPixelColor(i, purple_to_orange[i]);
}
}else{
for(int i = 0; i < (lifeTotalOne%20); i++){
strip1.setPixelColor(i, purple_to_orange[i]);
}
}
Serial.print(">20 | LifeTotalOne: ");
Serial.println(lifeTotalOne);
}
strip1.show();
if(lifeTotalTwo >60 && lifeTotalTwo <=80 ){
for(int i = 0; i < 20; i++){
strip2.setPixelColor(i, 0x000000);
}
if((lifeTotalTwo%20)==0){
for(int i = 0; i < 20; i++){
strip2.setPixelColor(i, purple_to_orange[i]);
}
}else{
for(int i = 0; i < (lifeTotalTwo%20); i++){
strip2.setPixelColor(i, purple_to_orange[i]);
}
}
}
strip2.show();
// above 80
if(lifeTotalOne >80){
for(int i = 0; i < 20; i++){
strip1.setPixelColor(i, 0x000000);
}
if((lifeTotalOne%20)==0){
for(int i = 0; i < 20; i++){
strip1.setPixelColor(i, purple_to_orange[19]);
}
}else{
for(int i = 0; i < (lifeTotalOne%20); i++){
strip1.setPixelColor(i, purple_to_orange[19]);
}
}
Serial.print(">20 | LifeTotalOne: ");
Serial.println(lifeTotalOne);
}
strip1.show();
if(lifeTotalTwo >80){
for(int i = 0; i < 20; i++){
strip2.setPixelColor(i, 0x000000);
}
if((lifeTotalTwo%20)==0){
for(int i = 0; i < 20; i++){
strip2.setPixelColor(i, purple_to_orange[19]);
}
}else{
for(int i = 0; i < (lifeTotalTwo%20); i++){
strip2.setPixelColor(i, purple_to_orange[19]);
}
}
}
strip2.show();
}