
To interaktywna gra zręcznościowa i pamięciowa oparta na mikrokontrolerze ESP8266 (NodeMCU) oraz pasku LED WS2812. Celem gracza jest zapamiętanie i poprawne odtworzenie rosnącej sekwencji kolorów zapalających się na diodach.
Gra ćwiczy:
-
pamięć krótkotrwałą,
-
czas reakcji,
-
koncentrację i koordynację wzrokowo-ruchową.
1. Płytka ESP8266 (NodeMCU)
2. Pasek LED
3. Buzzer
Projekt to interaktywna gra elektroniczna łącząca elementy zręcznościowe i pamięciowe. Bazuje na mikrokontrolerze NodeMCU z modułem ESP8266, adresowalnym pasku LED WS2812, zestawie przycisków oraz opcjonalnym buzzerze do sygnalizacji dźwiękowej. Gracz ma za zadanie obserwować i zapamiętywać sekwencję kolorów zapalających się na diodach LED, a następnie odtworzyć ją przy użyciu przypisanych przycisków.
Wraz z postępem w grze, sekwencja staje się coraz dłuższa i trudniejsza, aż do osiągnięcia maksymalnej długości. Gra kończy się dopiero w momencie popełnienia błędu, który sygnalizowany jest miganiem diod w kolorze czerwonym oraz dźwiękiem.
#include <Adafruit_NeoPixel.h>
#define LED_PIN 6
#define LED_COUNT 8
#define BUTTON_BLUE 2
#define BUTTON_PURPLE 7
#define BUTTON_ORANGE 4
#define BUTTON_GREEN 8
#define BUZZER_PIN 9
#define MAX_SEQUENCE_LENGTH LED_COUNT
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
enum Color { BLUE, PURPLE, ORANGE, GREEN, NONE };
Color sequence[MAX_SEQUENCE_LENGTH];
int sequenceLength = 3;
int playerIndex = 0;
bool showingSequence = true;
unsigned long lastStepTime = 0;
int showIndex = 0;
unsigned long reactionStartTime = 0;
void setup() {
pinMode(BUTTON_BLUE, INPUT_PULLUP);
pinMode(BUTTON_PURPLE, INPUT_PULLUP);
pinMode(BUTTON_ORANGE, INPUT_PULLUP);
pinMode(BUTTON_GREEN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT); // Buzzer
strip.begin();
strip.show();
Serial.begin(9600);
delay(1000);
startupAnimation(); // ✨ animacja i dźwięk startowy
Serial.println("🎮 Simon Says – klikaj po zapaleniu diod, ale nie za wcześnie!");
startNewRound();
}
void loop() {
// Pokazywanie sekwencji
if (showingSequence) {
if (millis() - lastStepTime > 600) {
if (showIndex > 0) {
strip.setPixelColor(showIndex - 1, 0); // zgaś poprzednią
}
if (showIndex < sequenceLength) {
showColorOnLed(showIndex, sequence[showIndex]);
lastStepTime = millis();
showIndex++;
} else {
strip.clear();
strip.show();
showingSequence = false;
Serial.println("👉 Możesz odtwarzać sekwencję.");
}
}
}
// Obsługa kliknięć
static bool buttonReleased = true;
Color pressed = getPressedButton();
if (pressed == NONE) {
buttonReleased = true;
return;
}
if (buttonReleased) {
buttonReleased = false;
// Zbyt wcześnie (jeszcze nie pokazany kolor)
if (playerIndex >= showIndex) {
Serial.println("❌ Błąd! Kliknięcie przed pokazaniem koloru.");
errorFeedback();
sequenceLength = 3;
startNewRound();
return;
}
// Poprawny klik
if (pressed == sequence[playerIndex]) {
if (playerIndex == 0) {
reactionStartTime = millis(); // start pomiaru czasu
}
playerIndex++;
if (playerIndex == sequenceLength) {
unsigned long reactionTime = millis() - reactionStartTime;
Serial.print("✅ Sekwencja poprawna! Czas reakcji: ");
Serial.print(reactionTime);
Serial.println(" ms");
delay(1000);
if (sequenceLength < MAX_SEQUENCE_LENGTH) {
sequenceLength++;
}
startNewRound();
}
} else {
Serial.println("❌ Błąd! Zła sekwencja.");
errorFeedback();
sequenceLength = 3;
startNewRound();
}
delay(250); // opóźnienie, by uniknąć szybkich powtórzeń
}
}
void startNewRound() {
strip.clear();
strip.show();
showingSequence = true;
showIndex = 0;
playerIndex = 0;
lastStepTime = millis();
Serial.print("\n🧠 Nowa runda. Długość sekwencji: ");
Serial.println(sequenceLength);
for (int i = 0; i < sequenceLength; i++) {
sequence[i] = static_cast<Color>(random(0, 4));
}
}
Color getPressedButton() {
if (digitalRead(BUTTON_BLUE) == LOW) { delay(30); if (digitalRead(BUTTON_BLUE) == LOW) return BLUE; }
if (digitalRead(BUTTON_PURPLE) == LOW) { delay(30); if (digitalRead(BUTTON_PURPLE) == LOW) return PURPLE; }
if (digitalRead(BUTTON_ORANGE) == LOW) { delay(30); if (digitalRead(BUTTON_ORANGE) == LOW) return ORANGE; }
if (digitalRead(BUTTON_GREEN) == LOW) { delay(30); if (digitalRead(BUTTON_GREEN) == LOW) return GREEN; }
return NONE;
}
void showColorOnLed(int ledIndex, Color color) {
uint32_t c;
switch (color) {
case BLUE: c = strip.Color(0, 0, 255); break;
case PURPLE: c = strip.Color(128, 0, 128); break;
case ORANGE: c = strip.Color(255, 80, 0); break;
case GREEN: c = strip.Color(0, 255, 0); break;
default: c = 0;
}
strip.setPixelColor(ledIndex, c);
strip.show();
}
void errorFeedback() {
for (int i = 0; i < 3; i++) {
analogWrite(BUZZER_PIN, 127); // Włącz buzzer
for (int j = 0; j < LED_COUNT; j++) {
strip.setPixelColor(j, strip.Color(255, 0, 0));
}
strip.show();
delay(200);
analogWrite(BUZZER_PIN, 0); // Wyłącz buzzer
strip.clear();
strip.show();
delay(200);
}
}
void startupAnimation() {
// Zapalaj diody po kolei na biało
for (int i = 0; i < LED_COUNT; i++) {
strip.setPixelColor(i, strip.Color(255, 255, 255));
strip.show();
delay(150);
}
// Długi dźwięk startowy
analogWrite(BUZZER_PIN, 127);
delay(800);
analogWrite(BUZZER_PIN, 0);
strip.clear();
strip.show();
}