Laby­rinth-Spiel mit TFT und Fernbedienung

Lese­zeit: 8 Minu­ten

Lösung
Seite als PDF




Mit Hil­fe einer Fern­be­die­nung wird auf einem TFT-Moni­tor ein Ball durch ein klei­nes Laby­rinth bewegt. Beim Druck auf die *-Tas­te wird die Zeit­mes­sung gestartet.

So sieht es aus:

Benö­tig­te Bauteile:

  • Keyes Fern­be­die­nung
  • Infra­rotem­pfän­ger
  • Adafruit 1,8 Zoll TFT ST7735
  • Lei­tungs­dräh­te

Baue die Schal­tung auf:

1 -> Gnd -> GND
2 -> VCC -> 5V
3 -> RESET -> D9
4 -> D/C -> D8
5 -> CARD_CS (nicht ange­schlos­sen)
6 -> TFT_CS -> D10
7 -> SDO -> D11
8 -> SCK -> D13
9 -> SDI (nicht ange­schlos­sen)
10 -> LITE ->5V

Pin­be­le­gung Adaf­ruit 1,8 Zoll TFT ST7735

Das ver­wen­de­te TFT-Modul von Adafruit hat eine Bild­schirm­dia­go­na­le von 1,8 Zoll und eine Bild­schirm­auf­lö­sung von 160×128 Pixeln.

Ach­te auf die Pin­be­le­gung der Infrarotempfänger.

Ach­te dar­auf, dass die Bat­te­rie rich­tig ein­ge­legt wur­de. Der Minus-Pol liegt oben.

Die Fern­be­die­nung sen­det beim Druck auf die Tas­ten einen Zahlencode.

Tas­ten­codes Keyes-Fernbedienung

Pfeil oben
Tas­ten­code70
Pfeil linksTas­te OKPfeil rechts
Tas­ten­code686467
Pfeil unten
Tas­ten­code21
Tas­te 1Tas­te 22Tas­te 3
Tas­ten­code222513
Tas­te 4Tas­te 5Tas­te 6
Tas­ten­code122494
Tas­te 7Tas­te 8Tas­te 9
Tas­ten­code82890
Tas­te *Tas­te 0Tas­te #
Tas­ten­code668274

➨Tas­ten­codes Open­Smart Fernbedienung

➨Test­pro­gramm belie­bi­ge Fernbedienung

Benö­tig­te Biblio­theken:

Die Tas­ten­codes kannst du mit fol­gen­dem Pro­gramm her­aus­fin­den. Sie wer­den im Seri­el­len Moni­tor angezeigt. 

// benötigte Bibliothek einbinden
# include "IRremote.h"

// der Pin, an dem der Infrarot-Empfänger angeschlossen ist
int EmpfaengerPin = 11;

void setup() 
{
  // Seriellen Monitor starten
  Serial.begin(9600);
  
  // Infrarot-Empfänger starten
  IrReceiver.begin(EmpfaengerPin);
}

void loop() 
{
  // decode() -> Daten lesen
  if (IrReceiver.decode()) 
  {
    // kurzes delay, damit nur ein Tastendruck gelesen wird
    
    delay(200);

    // resume -> nächsten Wert lesen
    IrReceiver.resume();

    /*
      der Empfänger empfängt zwischendurch Signale, 
      die nicht ausgewertet werden können
      es sollen dehalb nur die korrekt erkannten Tasten ausgewertet werden
      die Dezimalwerte der korrekten erkannten Tasten liegen zwischen > 0 und < 95
      es wird abgefragt, ob das empfangene Kommando decodedIRData.command
      zwischen 0 und (&&) 95 liegt
    */
    if (IrReceiver.decodedIRData.command > 0 && IrReceiver.decodedIRData.command < 95) 
    {
      Serial.print("Dezimalwert: ");

      // IrReceiver.decodedIRData.command = Wert der gedrückten Taste
      Serial.print(IrReceiver.decodedIRData.command);
      Serial.print(" -> ");

      // Werte abfragen und anzeigen
      if (IrReceiver.decodedIRData.command == 22) Serial.println("Taste 1");
      if (IrReceiver.decodedIRData.command == 25) Serial.println("Taste 2");
      if (IrReceiver.decodedIRData.command == 13) Serial.println("Taste 3");
      if (IrReceiver.decodedIRData.command == 12) Serial.println("Taste 4");
      if (IrReceiver.decodedIRData.command == 24) Serial.println("Taste 5");
      if (IrReceiver.decodedIRData.command == 94) Serial.println("Taste 6");
      if (IrReceiver.decodedIRData.command == 8) Serial.println("Taste 7");
      if (IrReceiver.decodedIRData.command == 28) Serial.println("Taste 8");
      if (IrReceiver.decodedIRData.command == 90) Serial.println("Taste 9");
      if (IrReceiver.decodedIRData.command == 82) Serial.println("Taste 0");
      if (IrReceiver.decodedIRData.command == 66) Serial.println("Taste *");
      if (IrReceiver.decodedIRData.command == 74) Serial.println("Taste #");
      if (IrReceiver.decodedIRData.command == 68) Serial.println("Pfeil links");
      if (IrReceiver.decodedIRData.command == 67) Serial.println("Pfeil rechts");
      if (IrReceiver.decodedIRData.command == 70) Serial.println("Pfeil oben");
      if (IrReceiver.decodedIRData.command == 21) Serial.println("Pfeil unten");
      if (IrReceiver.decodedIRData.command == 64) Serial.println("OK");
    }
  }
}

Funk­tio­nen der Biblio­thek Adafruit ST7735

Schlüs­sel­wortPara­me­terAkti­on
width();Bild­schirm­brei­te feststellen
height();Bild­schirm­hö­he feststellen
begin()TFT star­ten
initR(initR(INITR_*TAB););BLACKTAB
GREENTAB
REDTAB
Farb­sche­ma bestimmen
setRotation(Richtung);Rich­tung = 0 → nicht drehen
Rich­tung = 1 → 90° drehen
Rich­tung = 2 → 180° drehen
Rich­tung = 3 → 270 ° drehen
Bild­schirm ausrichten
fillScreen(Farbe);Stan­dard­far­ben:
ST7735_BLACK
ST7735_WHITE
ST7735_GREEN
ST7735_RED
ST7735_BLUE
ST7735_YELLOW
ST7735_ORANGE
ST7735_MAGENTA
ST7735_CYAN
Bild­schirm­hin­ter­grund
drawLine(StartX, Star­tY, End­eX, EndeY, Farbe);Linie zeich­nen
drawFastHLine(StartX, Star­tY, Län­ge, Farbe);hori­zon­ta­le Linie zeichnen
drawFastVLine(StartX, Star­tY, Län­ge, Farbe);ver­ti­ka­le Linie zeichnen
drawRect(StartX, Star­tY,, Brei­te, Höhe, Farbe);Recht­eck zeichnen
drawRoundRect(StartX, Star­tY, Brei­te, Höhe, Ecken­ra­di­us, Farbe);abge­run­de­tes Recht­eck zeichnen
fillRect(StartX, Star­tY, Brei­te, Höhe, Füllfarbe);aus­ge­füll­tes Recht­eck zeichnen
drawCircle(MittelpunkX, Mit­tel­punk­tY, Radi­us, Farbe);Kreis zeich­nen
fillCircle(MittelpunktX, Mit­tel­punk­tY, Radi­us, Füllfarbe);Aus­ge­füll­ten Kreis zeichnen
setCursor(x, y);Cur­sor setzen
setTextSize(Textgröße);Text­grö­ße:
1 - 4
Text­grö­ße bestimmen
setTextColor(Farbe);Text­far­be setzen
print("Text"); println("Text");Text schrei­ben
setTextWrap(true/false);fal­se → Text fließt über den Rand des TFTs hinaus
true → Text wird am Ende umgebrochen
Zei­len­um­bruch

Bei­spiel mit Gra­fik und Text

So sieht es aus:

Adres­sie­rung der Bildpunkte

Bin­de die benö­ti­gen Biblio­the­ken ein und defi­nie­re die Varia­blen. Beach­te die Kommentare.

/*
  Pinbelegung:
  GND      (1) - GND
  VCC      (2) - 5V
  RESET    (3) - D9
  D/C      (4) - D8
  CARD-CS  (5) -
  TFT-CS   (6) - D10
  SDO     (7) - D11
  SCK      (8) - D13
  SDI     (9) -
  LITE    (10) - 5V
*/

# include "IRremote.h"
int EmpfaengerPin = 6;

# include "Adafruit_GFX.h"
# include "Adafruit_ST7735.h"
# include "SPI.h"

// Pins definieren
# define TFT_CS        10
# define TFT_RST        9
# define TFT_DC         8

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
/*
  Farben als hexadezimal definiert
  alternativ:
  int SCHWARZ = 0;
  int BLAU = 15;
  . . .
*/
// Farben
# define SCHWARZ    0x0000 // dezimal 0
# define BLAU       0x000F // dezimal 15
# define ROT        0xF800 // dezimal 406664
# define GRUEN      0x0E81 // dezimal 3713
# define CYAN       0x07FF // dezimal 2047
# define MAGENTA    0xF81F // dezimal 63519
# define GELB       0xAFE5 // dezimal 65504
# define WEISS      0xFFFF // dezimal 65535
# define BRAUN      0xFC00 // dezimal 64512
# define GRAU       0xF7F0 // dezimal 63472
# define GRUENGELB  0xAFE5 // dezimal 45029
# define DUNKELCYAN 0x03EF // dezimal 1007
# define ORANGE     0xFD20 // dezimal 64800
# define PINK       0xFC18 // dezimal 64536

// Farbe der Blöcke
# define FARBE GRUEN

// Farbe des Kreises
# define KREISFARBE GELB

// Farbe der Schrift
# define SCHRIFTFARBE WEISS

// Spiel starten wenn * gedrückt wurde
bool SpielStart = false;

// Radius des kreises
const int Radius = 10;

// Abstand zu den Rändern
const int Abstand = Radius * 2;

// je höher, dest langsamer
const int Geschwindigkeit = 100;

// Bewegung des Kreises in Pixeln
const int Bewegung = 5;

// Startposition des Kreises
int CursorX = Radius;
int CursorY = tft.height() / 2 - Abstand;

// Variable für die Zeitmessung
long Start;

Der set­up-Teil:

void setup()
{
  // Startbildschirm
  // schwarzes Farbschema vertkale Ausrichtung (nicht drehen)
  // Cursor setzen, Schriftgröße und -farbe definieren
  tft.initR(INITR_BLACKTAB);
  tft.setRotation(0);
  tft.fillScreen(SCHWARZ);
  tft.setTextSize(2);
  tft.setCursor(1, 10);
  tft.setTextColor(ROT);

  tft.println("Start:");
  tft.print("-> *");
  IrReceiver.begin(EmpfaengerPin);
}

Der loop-Teil. Beach­te die Kommentare.

void loop()
{
  if (IrReceiver.decode())
  {
    delay(100);

    // nächsten Wert lesen
    IrReceiver.resume();

    // Start mit *
    if (IrReceiver.decodedIRData.command == 0x42)
    {
      // Spiel wird gestartet
      SpielStart = true;

      // Parcours bauen
      ParcoursBauen();

      // Zeitmessung starten
      Start = millis();
    }
  }

  // wenn der Button * gedrückt wurde
  if (SpielStart)
  {

    // Taste OK
    if (IrReceiver.decodedIRData.command == 0x40)
    {
      // Kreis an der aktuellen Position anhalten
      tft.fillCircle(CursorX, CursorY, Radius, KREISFARBE);
    }

    // Taste nach oben
    if (IrReceiver.decodedIRData.command == 0x46)
    {
      // Kreis an der aktuellen Position "löschen"
      tft.fillCircle(CursorX, CursorY, Radius, SCHWARZ);

      // wenn der Bildschirmrand oben noch nicht erreicht wurde
      // rückwärts -> Richtung x = 1 bewegen
      if (CursorY > Radius) CursorY -= Bewegung;

      // Kreis an der neuen Position zeichnen
      tft.fillCircle(CursorX, CursorY, Radius, KREISFARBE);
      delay(Geschwindigkeit);
    }

    // Taste nach unten
    if (IrReceiver.decodedIRData.command == 0x15)
    {
      // Kreis an der aktuellen Position "löschen"
      tft.fillCircle(CursorX, CursorY, Radius, SCHWARZ);

      // wenn der Bildschirmrand rechts noch nicht erreicht wurde
      // vorwärts -> Richtung tft.height() bewegen
      if (CursorY < tft.height() - Radius) CursorY += Bewegung;

      // Kreis an der neuen Position zeichnen
      tft.fillCircle(CursorX, CursorY, Radius, KREISFARBE);
      delay(Geschwindigkeit);
    }

    // Taste nach links
    if (IrReceiver.decodedIRData.command == 0x44)
    {
      // Kreis an der aktuellen Position "löschen"
      tft.fillCircle(CursorX, CursorY, Radius, SCHWARZ);

      // wenn der Bildschirmrand links noch nicht erreicht wurde
      // rückwärts -> Richtung linken Bildschirmrand bewegen
      if (CursorX > Radius) CursorX -= Bewegung;

      // Kreis an der neuen Position zeichnen
      tft.fillCircle(CursorX, CursorY, Radius, KREISFARBE);
      delay(Geschwindigkeit);
    }

    // Taste nach rechts
    if (IrReceiver.decodedIRData.command == 0x43)
    {
      // Kreis an der aktuellen Position "löschen"
      tft.fillCircle(CursorX, CursorY, Radius, SCHWARZ);

      // Abfrage, ob der rechte Rand erreicht wurde, nicht nötig
      // wird in der nächsten bedingung abgefragt
      CursorX += Bewegung;

      // Kreis an der neuen Position zeichnen
      tft.fillCircle(CursorX, CursorY, Radius, KREISFARBE);
      delay(Geschwindigkeit);
    }

    // rechter Bildschirmrand erreicht -> Spielende
    if (CursorX > tft.height() - Radius)
    {
      ErgebnisZeigen();
    }
  }
}

Jetzt feh­len noch die ➨Funk­tio­nen Ergeb­nis­Zei­gen() und ParcoursBauen():

void ErgebnisZeigen()
{
  // Zeit berechnen
  int Sekunden;
  long VerstricheneZeit = millis() - Start;
  Sekunden = int(VerstricheneZeit / 1000);

  // Zeit anzeigen
  tft.fillScreen(SCHWARZ);
  tft.setTextSize(2);
  tft.setCursor(1, 10);
  tft.println("Zeit:");
  tft.println(String(Sekunden) + " s");

  tft.setCursor(1, 40);
  tft.setTextColor(ROT);
  tft.println();
  tft.println("Neustart:");
  tft.println("-> *");
  SpielStart = false;
}

void ParcoursBauen()
{
  CursorX = Radius;
  CursorY = tft.height() / 2 - Abstand;
  tft.fillScreen(SCHWARZ);

  // Kreis anzeigen
  tft.fillCircle(CursorX, CursorY, Radius, KREISFARBE);

  // Parcours "bauen"
  tft.fillRect(65, 35, 5, 45, FARBE);
  tft.fillRect(1, 1, 35, 35, FARBE);
  tft.fillRect(1, 80, 70, 80, FARBE);
  tft.fillRect(110, 1, 70, 95, FARBE);
  tft.fillRect(110, 130, 140, 160, FARBE);
}

Startseite
Aufgaben A-Z
Suchen
Downloads
Fehlermeldungen
Seite als PDF

Ver­wand­te Aufgaben:


Letzte Aktualisierung: 6. Okt 2023 @ 13:34