/*
Simon Says Spiel für einen Geocache
Simon Says ist ein Reaktions- und Gedächtnisspiel, bei dem eine Abfolge von blinkenden LEDs gezeigt wird.
Der Spieler muss diese Reihenfolge durch Tastendrücke korrekt wiederholen.
Mit jeder Runde wird die Sequenz länger und dadurch schwieriger.
Geogaching ist eine Globale Schatzsuche bei dem man versuchen muss mithilfe von Beschreibungen und Koordinaten einen sogenannten Geocache suchen.
Diser Geocache ist meist eine Art Dose welche ein Logbuch enthält.
*/
//Bibliotheken Einbinden
#include <Wire.h> //I²C Bus Bibliothek
#include <SPI.h> //SPI Bus Bibliothek
#include <U8g2lib.h> //Bibliothek des Displays
#include <SD.h> //Bibliothek für den SD Karten Leser
//Initialisierungen
U8G2_SH1107_128X128_F_HW_I2C u8g2(U8G2_R0); // Display Festlegen
//Eingänge
const int ButtonsX[] = {32, 33, 25}; //Pins der Buttonmatrix auf der X Achse
const int EncoderCLK = 4; //CLK Pin des Encoders
const int EncoderDT = 16; //DT Pin des Encoders
const int EncoderSW = 17; //SW Pin des Encoders
//Ausgänge
const int ButtonsY[] = {26, 27, 14}; //Pins der Buttonmatrix auf der Y Achse
const int Buzzer = 0; //Passiver Buzzer der Passend einen Ton macht
const int SDSelect = 5; //SD Karte auswählen
//Textvariablen
String Username; //Endgültiger Benutzernamen
char InputText[21] = ""; //Aktuell ausgewähltes Zeichen bei der Benutzereingabe
char DisplayText[21] = ""; //Bisher eingetragene Zeichen bei der Benutzereingabe
const char* Characters[] = //Erlaubte Zeichen für die Benutzereingabe
{
"a","b","c","d","e","f","g","h","i","j","k","l","m",
"n","o","p","q","r","s","t","u","v","w","x","y","z",
"0","1","2","3","4","5","6","7","8","9","-","_","'",
"<"
};
//Allgemeine Parameter
const int NumberOfRounds = 2; //Anzahl der Runden
const int Time = 300; //Allgemeine Verzögerung
//Paramter für die Melodien des Buzzers
const int Tones[] = {262, 294, 330, 370, 415, 466, 494, 554, 622}; //Tonhöhen bei bestimmten LED's bzw Buttons
const int RightMelody[] = {587, 698, 880}; //Tonhöhen bei der Melodie die Mitteilt das eine Runde Richtig war
const int RightMelodyDuration[] = {200, 200, 200}; //Verzögerung bei der Melodie die Mitteilt das eine Runde Richtig war
const int LoseMelody[] = {196, 130, 98}; //Tonhöhen bei der Melodie die Mitteilt das etwas falsches gedrückt wurde
const int LoseMelodyDuration[] = {300, 300, 200}; //Verzögerungen bei der Melodie die Mitteilt das etwas falsches gedrückt wurde
const int WinMelody[] = {523, 659, 784, 523, 523, 659, 784, 1047}; //Tonhöhen bei der Melodie die Mitteilt das das Spiel eroflgreich abgeschlossen wurde
const int WinMelodyDuration[] = {262, 262, 262, 525, 262, 262, 262, 787}; //Verzögerungen bei der Melodie die Mitteilt das das Spiel eroflgreich abgeschlossen wurde
//Parameter für das Aufzeichnen des Simon Says Feld
const int GridSize = 40; //Grösse der Felder
const int GridX[] = {0, 43, 86, 0, 43, 86, 0, 43, 86}; //Position der Felder auf der X Achse
const int GridY[] = {0, 0, 0, 43, 43, 43, 86, 86, 86}; //Position der Felder auf der Y Achse
//Variablen
int Step; //Variable für Schrittkette
int PressedButton;
int LastPressedButton; //Index des Letzten Gedrückten Buttons
int InputCount; //Anzahl der Gedrücken Buttons Innerhalb einer Runde
int Round; //Aktuelle Runde
int SavedSequence[NumberOfRounds]; //Array um die Reihenfolge der LED's zu Speichern
int CurrentCLKState; //Aktueller Status des CLK Pins am Encoder
int LastCLKState; //Letzter Status des CLK Pins am Encoder
int LastSWState; //Letzer Status des SW Pins am Encoder
int CounterCharakters; //Anzahl bisher eingetragenen Zeichen (maximal 20)
int CursorPosition; //Cursor Position auf dem Disply -> Benutzername eingabe
int Attempts; //Anzahl Versuche des Spielers bis (wird nachdem es geschafft wurde zurückgesetzt)
unsigned long Lastmillis1; //Variable für die millis() funktion
unsigned long Lastmillismelody; //Variable für die millis() funktion
//Array Grössen
const int MatrixSize = sizeof(ButtonsX) / sizeof(ButtonsX[0]); //Anzahl der Button in eine Richtung -> Funktioniert nur Richig bei gleichmässiger Verteilung auf X und Y
const int NumberOfButtons = sizeof(ButtonsX) / sizeof(ButtonsX[0]) * sizeof(ButtonsY) / sizeof(ButtonsY[0]); // Anzahl der Buttons
const int CharactersSize = sizeof(Characters) / sizeof(Characters[0]) - 1; //Grösse des Array für die Zeichen bei der Benutzereingabe
const int RightMelodySize = sizeof(RightMelody) / sizeof(RightMelody[0]);
const int LoseMelodySize = sizeof(LoseMelody) / sizeof(LoseMelody[0]);
const int WinMelodySize = sizeof(WinMelody) / sizeof(WinMelody[0]);
//Struct für Melodien
struct Melody {
const int* Melody;
const int* Duration;
int Size;
};
//Melodien in einem Array zusammengefasst
const Melody AllMelodies[] = {
{RightMelody, RightMelodyDuration, RightMelodySize},
{LoseMelody , LoseMelodyDuration , LoseMelodySize},
{WinMelody , WinMelodyDuration , WinMelodySize}
};
//Setup wird nur beim Start einmal ausgeführt
void setup() {
//Eingänge / Ausgänge definieren
pinMode(EncoderCLK, INPUT);
pinMode(EncoderDT, INPUT);
pinMode(EncoderSW, INPUT_PULLUP);
pinMode(Buzzer, OUTPUT);
for (int i = 0; i < MatrixSize; i++) {
pinMode(ButtonsX[i], INPUT_PULLUP);
pinMode(ButtonsY[i], OUTPUT);
digitalWrite(ButtonsY[i], HIGH); //Ausgänge Initialisieren
}
noTone(Buzzer);
//Merker Initialisieren
Step = 0;
Round = 0;
PressedButton = 0;
LastPressedButton = 0;
InputCount = 0;
CounterCharakters = 0;
CursorPosition = 0;
Attempts = 0;
CurrentCLKState = 0;
Lastmillis1 = 0;
LastSWState = false;
Username = "";
//Display Starten
u8g2.begin();
u8g2.clearBuffer();
u8g2.sendBuffer();
//SD Kartenleser Initialisieren
if (!SD.begin(SDSelect))
{
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 20, "SD-Karte Fehler!");
u8g2.sendBuffer();
}
}
//Loop wird immer wieder wiederholt
void loop() {
switch (Step) {
case 0:
noTone(Buzzer);
Round = 0;
PressedButton = 0;
LastPressedButton = 0;
InputCount = 0;
CounterCharakters = 0;
CursorPosition = 0;
Attempts++;
CurrentCLKState = 0;
LastSWState = false;
Username = "";
LastCLKState = digitalRead(EncoderCLK);
for (int i = 0; i < NumberOfRounds; i++) {
SavedSequence[i] = 0;
}
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(5, 20, "Encoder dreucken:");
u8g2.sendBuffer();
Step = 1;
break;
case 1:
if (digitalRead(EncoderSW) == LOW) {
Grid(10);
Lastmillis1 = millis();
Step = 2;
}
break;
case 2:
if (millis() - Lastmillis1 > 1000) {
Step = 3;
}
break;
case 3: // Abfolge Abspielen
InputCount = 0;
// Neues Feld generieren
SavedSequence[Round] = random(1, NumberOfButtons + 1);
// Ganze Sequenz abspielen (inkl. neuem Feld)
for (int i = 0; i <= Round; i++) {
DrawGrid(SavedSequence[i] - 1);
}
Step = 4; // Weiter zu Eingabephase
break;
case 4:
for (int y = 0; y < MatrixSize; y++) {
digitalWrite(ButtonsY[y], LOW);
for (int x = 0; x < MatrixSize; x++) {
if (digitalRead(ButtonsX[x]) == LOW) {
LastPressedButton = y * MatrixSize + x + 1;
InputCount++;
DrawGrid(LastPressedButton - 1);
Step = 5;
}
}
digitalWrite(ButtonsY[y], HIGH);
}
break;
case 5:
if (LastPressedButton == SavedSequence[InputCount - 1]) {
if (InputCount >= Round + 1) {
Round++;
if (Round >= NumberOfRounds) {
PlayMelody (2); //WinMelody
DrawUsernameDisplay ();
Step = 6;
} else {
PlayMelody (0); //Right
Step = 3;
}
} else {
Step = 4;
}
} else {
PlayMelody (1); //Lose
Step = 0;
}
break;
case 6:
CurrentCLKState = digitalRead(EncoderCLK);
if (LastCLKState == LOW && CurrentCLKState == HIGH) {
if (digitalRead(EncoderDT) == LOW) {
CounterCharakters++;
if (CounterCharakters > CharactersSize) {
CounterCharakters = 0;
}
} else {
CounterCharakters--;
if (CounterCharakters < 0) {
CounterCharakters = CharactersSize;
}
}
DrawUsernameDisplay ();
}
LastCLKState = CurrentCLKState;
if (digitalRead(EncoderSW) == LOW && !LastSWState) {
LastSWState = true;
Lastmillis1 = millis();
}
if (digitalRead(EncoderSW) == HIGH && LastSWState) {
LastSWState = false;
if (millis() - Lastmillis1 < 2000) {
if (strcmp(Characters[CounterCharakters], "<") == 0) {
if (CursorPosition > 0) {
CursorPosition--;
InputText[CursorPosition] = '\0';
}
} else {
if (CursorPosition < 20) {
InputText[CursorPosition] = *Characters[CounterCharakters];
CursorPosition++;
InputText[CursorPosition] = '\0';
}
}
} else {
Step = 7;
}
DrawUsernameDisplay ();
}
break;
case 7:
Username = String(InputText);
Step = 0;
break;
default:
Step = 0;
break;
}
}
//Funktion für das Abspielen der Melodien
void PlayMelody (int MelodyType) {
const Melody& M = AllMelodies[MelodyType];
for (int i = 0; i < M.Size; i++) {
tone(Buzzer, M.Melody[i]);
delay(M.Duration[i]);
noTone(Buzzer);
}
}
//Funktion für die Anzeige des Grids
void DrawGrid(int buttonIndex) {
Grid(buttonIndex);
tone(Buzzer, Tones[buttonIndex]);
delay(Time); //Dauer des Tons
Grid(NumberOfButtons + 1);
noTone(Buzzer);
delay(Time); //Dauer Zwischen den Tönen
}
void Grid(uint8_t Box) {
u8g2.clearBuffer();
for (int b = 0; b < NumberOfButtons; b++) {
if (b == Box) {
u8g2.drawBox(GridX[b], GridY[b], GridSize, GridSize);
} else {
u8g2.drawFrame(GridX[b], GridY[b], GridSize, GridSize);
}
}
u8g2.sendBuffer();
}
//Funktion um das Display zu Beschreiben bei der Benutzereingabe
void DrawUsernameDisplay ()
{
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(5, 20, "Benutzername:");
strncpy(DisplayText, InputText, CursorPosition);
DisplayText[CursorPosition] = *Characters[CounterCharakters];
DisplayText[CursorPosition + 1] = '\0';
u8g2.drawStr(5, 50, DisplayText);
u8g2.drawStr(5, 53, "__________________");
u8g2.sendBuffer();
}
Loading
grove-oled-sh1107
grove-oled-sh1107