Sensoduino                              

Beitrag von Wolfgang Triebig zum Spiele-Wettbewerb 2018                                                 

Elektronik-Labor  Literatur  Projekte  Lernpakete  Kalender                      

DSCN3522.JPG


Schon seit einiger Zeit habe ich den Plan, das Spiel Senso zu programmieren. Angeregt durch den diesjährigen Kalender-Contest habe ich den Plan nun umgesetzt. Das Spiel habe ich auf der Grundlage eines Arduino programmiert und dazu ein passendes Shield aufgebaut.

Funktion:
Nach dem Start leuchten zunächst alle Leuchtdioden. Damit ist das Spiel startbereit. Nachdem man eine beliebige Taste gedrückt hat, wird eine zufällige Farbenfolge generiert und es leuchtet zunächst eine Farbe kurz auf und es ertönt ein zugehöriger Ton. Widerholt man die richtige Farbe durch Tastendruck leuchtet die dazugehörige Farbe auf. Im weiteren Verlauf des Spieles wiederholt das Spiel die Farbenfolge mit jeweils einer weiteren Farbe. Nach dem Aufleuchten der Farbenfolge muss diese vom Spieler richtig wiederholt werden. Drückt man eine falsche Taste, ertönt ein Fehlerton und die richtige Farbe wird angezeigt. Das Spiel ist beendet. Alle Farben leuchten und warten auf ein neues Spiel. Nachdem man eine 10er-Folge richtig wiederholt hat, hat man die Runde gewonnen. Es ertönt eine Tusch. Alle Farben leuchten und das Spiel wartet auf den Start der nächsten Runde. Nach einem Tastendruck wird die neue Farbenfolge etwas schneller abgespielt. So steigert sich der Schwierigkeitsgrad von Runde zu Runde. Hat man 10 Runden erfolgreich gespielt, hat man gewonnen und man wird mit einem Lauflicht belohnt.

Shield:
Das Shield ist aus einem Stück Lochrasterplatine. Die Anschlüsse 8 ff. passen nicht in das Rastermaß von 2,54 mm sondern sind um ein halbes Raster verschoben. Die Postenstecker habe ich passend gebogen. Die Bedienblende ist ausgedruckt und laminiert. An Bauteilen enthält das Shield nur die vier Taster, die zugehörigen Pullup-Widerstände werden im Controller aktiviert, vier LED mit passenden Vorwiderständen, die unterschiedlichen Werte sind empirisch ermittelt um eine gleichmäßige Helligkeit der LED zu erhalten sowie ein kleiner Lautsprecher.

Software:
Durch Einbinden der Bibliothek pitches.h kann die Tonhöhe anstelle über die Frequenz, über Töne der Tonleiter festgelegt werden. Das macht die Auswahl um einiges leichter. Danach definiere ich die verschiedenen Ein- und Ausgänge. Ich bin von früher noch die Verwendung von #define  gewohnt, auch wenn const int dafür sorgt, dass Konstanten nur richtig verwendet werden können. Alle verwendeten Ports sind hier definiert, auch wenn nicht alle Definitionen im Programm benötigt werden. Das hat zwei Vorteile, einmal sind am Programmanfang alle verwendeten Port eingetragen um auch später mal das Programm noch verstehen zu können. Zum andern kann ich beim Programmieren direkt die Namen verwenden um muss nicht erst nachtragen wenn ich einen Wert benötige. Zwei Arrays sind für das Abspeichern der Farbenfolge und die Festlegung der Töne zuständig. Danach werden noch verschiedene Variablen deklariert.

Die void setup startet die serielle Ausgabe. Damit kann man später über den seriellen Monitor die Farbenfolge als Zahlenwerte nachlesen um das Spiel zu testen. Danach werden nur noch die Ein- und Ausgänge konfiguriert.

In der void loop werden zunächst alle LEDs eingeschaltet und das Programm wartet auf einen Tastendruck, danach wird über die Funktion NeueZahlen die Farbenfolge festgelegt. Die Farben werden durch folgende Zahlen repräsentiert:
 

Zahlwert

Farbe

1

Rot

2

Gelb

4

Grün

8

Blau

 

Dabei ist bei jeder Farbe jeweils nur ein Bit auf 1 gesetzt, so dass die LED über einen Port-Befehl angesteuert werden können. Die äußer Schleife sorgt dafür, dass die Zahlenfolge zehnmal angezeigt wird, während die erste innere Schleife festlegt, wie viele Farben angezeigt werden. Die Anzeige selbst erfolgt in der gleichnamigen Funktion. Die zu den Farben gehörenden Töne sind jeweils im Feld 1, 2 4, und 8 eingetragen, so dass für die Ausgabe der Töne keine Umrechnung erfolgen muss. Die zweite innere Schleife prüft, ob die Tastatureingaben richtig sind. Bei einer Falscheingabe erfolgt die Ausgabe der richtigen Farbe mit dem Fehlerton. Danach wird über die break-Anweisungen aus den Schleifen ausgebrochen.

Wird die Farbenfolge komplett richtig angezeigt, ertönt ein Tusch. Wenn die Anzeigezeit größer ist als 100 Millisekunden, wird die Anzeigezeit um 100 Millisekunden reduziert und die loop-Schleife beginnt von neuem. Ist die Anzeigezeit bereits 100 Millisekunden, hat der Spieler 10 Folgen richtig durchspielt und wird mit einen Lauflicht belohnt.

Die drei Funktionen Tusch, Anzeige und NeueZahlen sind recht offen definiert, so dass diese Funktionen auch für andere Spiele weiter verwendet werden können.
 

/**************************************************\

* Name: Sensoduino                                 *

* Version 1.0                                      *

* Datum: 27.12.2018                                *

* Autor: Wolfgang Triebig                          *

\**************************************************/

 

#include "pitches.h"

 

#define TasteRt 3

#define TasteGe 4

#define TasteGn 5

#define TasteBl 6

#define LedRt 8

#define LedGe 9

#define LedGn 10

#define LedBl 11

#define Piezo 12

#define ZeitHand 300  //Anzeigezeit für Tastendruck

 

int Werteliste[10];   //Array für die Farbenfolge

int Ton[9]            //Array für Töne

{0,                   //ungenutzt

NOTE_C5,              //Ton für rot

NOTE_D5,              //Ton für gelb

NOTE_C3,              //Ton für Fehler

NOTE_E5,              //Ton für grün

NOTE_A5,              //1. Ton für Fanfare

NOTE_B5,              //2. Ton für Fanfare

NOTE_C6,              //3. Ton für Fanfare

NOTE_F5};             //Ton für blau

 

int i;                //Schleifenvariable

int Runde;            //Spielrunde

int Position;         //Aktuelle Stelle, aus der Werteliste, die angezeigt wird

int ZeitZeigen;       //Anzeigezeit für Farbe/Ton

int ZeitPause=100;    //Pausenzeit zwischen zwei Farben/Tönen

boolean Richtig;      //HIGH wenn richtige Taste, LOW wenn falsche Taste gedrückt wurde

 

void setup()

{

  Serial.begin(9600); //Für Testzwecke

  noTone(Piezo);

  for(i=TasteRt;i<=TasteBl;i++) //Inizialisierung der Tasteingänge

  {

    pinMode(i,INPUT);

    digitalWrite(i,HIGH);

  }

  for(i=LedRt;i<=Piezo;i++) //Inizializierung der Ausgänge (LED und Piezo)

  {

    pinMode(i,OUTPUT);

  }

}

 

void loop()

{

  PORTB=15; //Alle LEDs an

  while(0 ==(15 & ~(PIND/8)));  //Warten auf Eingabe zum Starten des Programmes

  PORTB=0;

  ZeitZeigen=1000;

  delay(ZeitZeigen);

  NeueZahlen(4);

  for(Runde=0;Runde<10;Runde++)  //Durchlauf von 10 Runden

  {

    for(Position=0;Position<=Runde;Position++)  //je Runde eine Farbe mehr anzeigen

    {

      Anzeige(ZeitZeigen,Werteliste[Position]);

    }

    for(Position=0;Position<=Runde;Position++)  //Jetzt kommt die Eingabe der Farbfolge

    {

      while(0 == (15 & ~(PIND/8))); //Warte auf Eingabe

      if((15 & ~(PIND/8)) == Werteliste[Position])  //Auswerten der Handeingabe

      {

        Anzeige(ZeitHand,Werteliste[Position]);

        Richtig=HIGH;

      }

      else

      {

        Anzeige(1000,3);

        Richtig=LOW;

      }

      while(0 != 15 & ~(PIND/8)); //Warten auf Ende der Handeingabe

      if(Richtig == LOW)  //Abbruch der Runde bei falscher Eingabe

      {

        break;

      }

    }

  if(Richtig == LOW)  //Abbruch des Spieles bei Falscheingabe

    break;

  delay(ZeitZeigen);

  }

  if(Richtig==HIGH)

  {

    Tusch();

    if(ZeitZeigen>100) //Wenn Runde richtig beendet, Tusch und kürzere Zeit für nächste Runde

    {

      ZeitZeigen=ZeitZeigen-100;

    }

    else   //Wenn alle Runden richtig beendet, Abschluss und neues Spiel

    {

      for(Runde=0;Runde<10;Runde++)

      {

        for(Position=1;Position<=8;Position=Position*2)

        {

          PORTB=Position;

          delay(200);

        }

        for(Position=8;Position>=1;Position=Position/2)

        {

          PORTB=Position;

          delay(200);

        }

      }

    }

  }

}

 

void Tusch()

{

  Serial.println("Tusch");

  PORTB=15;

  tone(Piezo, Ton[5], 200);

  delay(300);

  tone(Piezo, Ton[6], 200);

  delay(300);

  tone(Piezo, Ton[7], 750);

  delay(2000);

  PORTB=0;

}

 

void Anzeige(int Anzeigezeit,int Tonart)

{

  PORTB=Werteliste[Position];

  tone(Piezo, Ton[Tonart], Anzeigezeit);

  delay(Anzeigezeit);

  PORTB=0;

  delay(ZeitPause);

}

 

void NeueZahlen(int Wertebereich)

{

  Serial.println("Neue Zahlen");

  randomSeed(millis());

  for(int i=0;i<10;i++)

  {

    Werteliste[i] = 1;

    int Wert = random(Wertebereich);

    for(int j=0;j<Wert;j++)

    {

      Werteliste[i]=Werteliste[i]*2;

    }

  Serial.println(Werteliste[i]);

  }

}

 

Download: senso_1_0.zip


Während der Entwicklung mit einem Breadboard
     


Elektronik-Labor  Literatur  Projekte  Lernpakete  Kalender