Zum Inhalt

Simon Spiel

Da mir das programmieren von kleinen Spielen sehr Spaß macht, habe ich mal wieder etwas gesucht was man bauen könnte. Ich bin dann auf das Spiel Simon gekommen.

Hier mal ein Video von meiner provisorischen Variante:

Aufbau

Ich habe mich dafür entschieden die Taster aus transparentem PLA zu drucken und in dem Taster eine RGB-LED zu intigrieren. Somit dient die Eingabe gleichzeitig auch als Anzeige. Als Taster habe ich Kurzhubtaster auf eine Platine gelötet. Im Nachhinein stellte sich das als nicht so Optimal heraus, da die mechanischen Anforderungen relativ hoch sind. So ist bei mir der Druckpunkt der Taster nicht wirklich schön und ich muss im richtigen Winkel drücken (sieht man auch im Video ganz gut).

Andererseits ist das optische Ergebnis mit den RGB-LEDs und dem transparenten PLA echt super. Die WS2812B-LEDs habe ich in den Kunststoff eingedrückt, nachdem ich ihn über eine Heißluftpistole erwärmt habe.

Bilder


Zusammengebaut
Angeschaltete LEDs
Inhalt des Gehäuses
Platine mit LEDs
WS2812B-LED

Software

Es handelt sich hier um eines meiner ersten Projekte mit PlattformIO. Ich bereue es ehrlich gesagt, mich nicht schon früher damit beschäftigt zu haben. An externen Abhängigkeiten wird nur die Adafruit NeoPixel-Library verwendet.

An sich ist die Software mit einer State-Machine als Programmsteuerung aufgebaut. Damit lässt sich so ein Ablauf sehr einfach realisieren.

Es gibt diese vier Zustände:

intro       Startanimation
fail        Fehlerfall, warten auf Eingabe
input       Eingabe der gemerkten Farben
show        Vorspielen der neuen, zu merkenden Farben

Ablauf / Zustandsautomat

Quelltext

main.cpp

#include <Adafruit_NeoPixel.h>

#define PIN_LED 2
#define NUMPIXELS 4 

//LED-Key-Zuordnung
const uint8_t key[] = { 4, A2, 12, 7 };
const uint8_t led[] = { 2, 3, 1, 0 };


Adafruit_NeoPixel pixels(NUMPIXELS, PIN_LED, NEO_GRB + NEO_KHZ800);


//Funktion für LED aktiver/inaktiver Zustand
void set_led(int l, bool state) {
    uint32_t colors_en[] = { pixels.Color(0, 255, 0), pixels.Color(255, 0, 0), pixels.Color(0, 0, 255), pixels.Color(255, 255, 0) };
    uint32_t colors_dis[] = { pixels.Color(0, 50, 0), pixels.Color(50, 0, 0), pixels.Color(0, 0, 50), pixels.Color(25, 25, 0) };

    if(state) {
        pixels.setPixelColor(led[l], colors_en[l]);
    } else pixels.setPixelColor(led[l], colors_dis[l]);

    pixels.show();
}


void setup() {
    pixels.begin();

    randomSeed(analogRead(0)+analogRead(1)+analogRead(3)+analogRead(4));

    //Eingänge
    pinMode(key[0], INPUT); 
    pinMode(key[1], INPUT); 
    pinMode(key[2], INPUT); 
    pinMode(key[3], INPUT); 

    //Pullup
    digitalWrite(key[0], HIGH);
    digitalWrite(key[1], HIGH);
    digitalWrite(key[2], HIGH);
    digitalWrite(key[3], HIGH);
}

//Zustände
enum { intro, fail, input, show };
int state = intro;

int intro_ct = 50;
int error_red_val = 255;

uint8_t valuehistory[255];
uint8_t valuehistory_position = 0;

unsigned long timer1 = millis();

void loop() {
    //Introanimation
    if(state == intro) {
        int i = random(4);

        set_led(0, i==0);
        set_led(1, i==1);
        set_led(2, i==2);
        set_led(3, i==3);

        delay(100);

        intro_ct--;
        if(intro_ct == 0) state = fail; 
    }

    //Rot Blinken
    if(state == fail) {
        //Animation
        if(millis()-timer1 > 100) {
            error_red_val = (error_red_val==255) ? 50 : 255;

            timer1 = millis();
        }
        for (uint8_t i = 0; i < 4; i++)
        {
            pixels.setPixelColor(i, pixels.Color(error_red_val, 0, 0));
        }
        pixels.show();

        //reset saved values
        valuehistory_position = 0;

        //Tansaktion
        if(!digitalRead(key[0])||!digitalRead(key[1])||!digitalRead(key[2])||!digitalRead(key[3])) {
            state = show;

            set_led(0, false);
            set_led(1, false);
            set_led(2, false);
            set_led(3, false);

            delay(1000);
        }
    }

    //Abspielen
    if(state == show) {
        valuehistory[valuehistory_position] = random(4);

        for (uint8_t i = 0; i <= valuehistory_position; i++)
        {
            set_led(0, valuehistory[i]==0);
            set_led(1, valuehistory[i]==1);
            set_led(2, valuehistory[i]==2);
            set_led(3, valuehistory[i]==3);

            delay(500);

            set_led(0, false);
            set_led(1, false);
            set_led(2, false);
            set_led(3, false);

            delay(500);
        }

        state = input;
    }

    //Eingabe
    if(state == input) {
        for(uint8_t i = 0; i <= valuehistory_position; i++) {
            while(digitalRead(key[0])&&digitalRead(key[1])&&digitalRead(key[2])&&digitalRead(key[3]));

            bool correct = (valuehistory[i]==0&&!digitalRead(key[0])) || (valuehistory[i]==1&&!digitalRead(key[1])) || (valuehistory[i]==2&&!digitalRead(key[2])) || (valuehistory[i]==3&&!digitalRead(key[3]));

            set_led(0, !digitalRead(key[0]));
            set_led(1, !digitalRead(key[1]));
            set_led(2, !digitalRead(key[2]));
            set_led(3, !digitalRead(key[3]));

            delay(500);

            set_led(0, false);
            set_led(1, false);
            set_led(2, false);
            set_led(3, false);


            if(correct) {
                state = show;
                delay(100);
            } else {
                state = fail;
                break;
            }
        }

        delay(1000);

        valuehistory_position++;
    }  
}

3D Modelle

🧊 Taster 🧊 Gehäuse

📅 20.07.2020