Compare commits

...

2 Commits

Author SHA1 Message Date
f1f17cf754 Added puzzle color Remember 2026-01-15 21:22:23 +01:00
7ad21cd1d4 Reduce switch-on time to 100ms 2026-01-15 21:06:27 +01:00
4 changed files with 350 additions and 1 deletions

View File

@ -92,7 +92,7 @@ void handleEncoderMovement()
/* ========================================================= */
const int PIN_POWER_SWITCH_INPUT = 2;
const int PIN_POWER_SWITCH_OUTPUT = 12;
const unsigned long POWER_STABLE_THRESHOLD = 400;
const unsigned long POWER_STABLE_THRESHOLD = 100;
bool powerStableState;
bool powerLastRawState;
unsigned long powerLastChangeTime = 0;

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@ -0,0 +1,17 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nanoatmega328new]
platform = atmelavr
board = nanoatmega328new
framework = arduino
lib_deps =
adafruit/Adafruit NeoPixel@^1.15.2
robtillaart/PCF8575@^0.2.4

View File

@ -0,0 +1,327 @@
// ================== Projekt: Modul 2 ==================
// SPIELMECHANIK mit Gewinnanimation + Gewinnsignal
//
// Fix integriert:
// - zeitbasierte Entprellung pro Taster
// - verhindert Mehrfachregistrierung eines einzelnen Tastendrucks
#include <Arduino.h>
#include <Wire.h>
#include <PCF8575.h>
#include <Adafruit_NeoPixel.h>
// ------------------ Hardware ------------------
PCF8575 expander(0x20);
#define PIN_NEOPIXEL_BUTTONS 4
#define NUM_NEOPIXEL_BUTTONS 18
#define PIN_NEOPIXEL_LOESUNG 6
#define NUM_NEOPIXEL_LOESUNG 6
#define PIN_POWER_ON 13
#define PIN_TRIGGER_SHUFFLE 2
#define PIN_SEND_WIN 5
// ------------------ Timing ------------------
#define WIN_OFF_DELAY_MS 300
#define WIN_STEP_DELAY_MS 60
#define DEBOUNCE_TIME_MS 200 // <<< Entprellzeit
Adafruit_NeoPixel pixelsButtons(
NUM_NEOPIXEL_BUTTONS,
PIN_NEOPIXEL_BUTTONS,
NEO_RGB + NEO_KHZ800
);
Adafruit_NeoPixel pixelsSolution(
NUM_NEOPIXEL_LOESUNG,
PIN_NEOPIXEL_LOESUNG,
NEO_RGB + NEO_KHZ800
);
// ------------------ Mapping ------------------
enum ButtonSource { SRC_NANO, SRC_PCF8575 };
struct LedButtonMap {
uint8_t ledIndex;
ButtonSource source;
uint8_t pin;
};
const LedButtonMap ledButtonMap[18] = {
{ 0, SRC_NANO, A0 }, { 1, SRC_NANO, A1 }, { 2, SRC_NANO, A2 }, { 3, SRC_NANO, A3 },
{ 4, SRC_NANO, 7 }, { 5, SRC_NANO, 8 },
{ 6, SRC_PCF8575, 0 }, { 7, SRC_PCF8575, 1 }, { 8, SRC_PCF8575, 2 }, { 9, SRC_PCF8575, 3 },
{10, SRC_PCF8575, 4 }, {11, SRC_PCF8575, 5 }, {12, SRC_PCF8575, 6 }, {13, SRC_PCF8575, 7 },
{14, SRC_PCF8575, 8 }, {15, SRC_PCF8575, 9 }, {16, SRC_PCF8575,10 }, {17, SRC_PCF8575,11 }
};
// ------------------ Pride-Farben (dunkler) ------------------
const uint8_t prideColors[6][3] = {
{25, 0, 0},
{35, 10, 0},
{25, 25, 0},
{ 0, 25, 0},
{ 0, 0, 25},
{20, 0, 25}
};
// ------------------ Spielzustände ------------------
uint8_t solutionOrder[6];
uint8_t expectedOrder[6];
int8_t buttonAssignment[18];
bool winActive = false;
bool powerOnState = true;
bool lastPressed[18];
unsigned long ledOffTime[18];
unsigned long lastPressTime[18]; // <<< Entprellspeicher
uint8_t progressIndex = 0;
bool lastTriggerState = LOW;
// ------------------ Hilfsfunktionen ------------------
bool readButton(uint8_t i) {
if (ledButtonMap[i].source == SRC_NANO)
return digitalRead(ledButtonMap[i].pin) == LOW;
return expander.read(ledButtonMap[i].pin) == LOW;
}
// ------------------ Neues Spiel ------------------
void generateNewGame() {
for (uint8_t i = 0; i < 6; i++) solutionOrder[i] = i;
for (int i = 5; i > 0; i--) {
int j = random(i + 1);
uint8_t t = solutionOrder[i];
solutionOrder[i] = solutionOrder[j];
solutionOrder[j] = t;
}
for (uint8_t i = 0; i < 6; i++)
expectedOrder[i] = solutionOrder[5 - i];
for (uint8_t i = 0; i < 6; i++) {
uint8_t c = solutionOrder[i];
pixelsSolution.setPixelColor(
i,
pixelsSolution.Color(
prideColors[c][0],
prideColors[c][1],
prideColors[c][2]
)
);
}
pixelsSolution.show();
for (uint8_t i = 0; i < 18; i++) {
buttonAssignment[i] = -1;
pixelsButtons.setPixelColor(i, 0);
ledOffTime[i] = 0;
lastPressTime[i] = 0;
}
pixelsButtons.show();
uint8_t idx[18];
for (uint8_t i = 0; i < 18; i++) idx[i] = i;
for (int i = 17; i > 0; i--) {
int j = random(i + 1);
uint8_t t = idx[i];
idx[i] = idx[j];
idx[j] = t;
}
for (uint8_t i = 0; i < 6; i++)
buttonAssignment[idx[i]] = solutionOrder[i];
progressIndex = 0;
}
// ------------------ Gewinnanimation + Signal ------------------
void winAnimation() {
winActive = true;
digitalWrite(PIN_SEND_WIN, HIGH);
// 1) letzte korrekte Farbe stehen lassen
delay(500);
// 2) ALLES AUS (inkl. Off-Timer löschen)
for (uint8_t i = 0; i < 18; i++)
ledOffTime[i] = 0;
pixelsSolution.clear();
pixelsButtons.clear();
pixelsSolution.show();
pixelsButtons.show();
delay(WIN_OFF_DELAY_MS);
// 3) richtige Reihenfolge (umgekehrt), Lösung + Taster synchron
for (int i = 5; i >= 0; i--) {
uint8_t color = solutionOrder[i];
// Lösungs-LED
pixelsSolution.setPixelColor(
i,
pixelsSolution.Color(
prideColors[color][0],
prideColors[color][1],
prideColors[color][2]
)
);
// zugehöriger Taster
for (uint8_t b = 0; b < 18; b++) {
if (buttonAssignment[b] == color) {
pixelsButtons.setPixelColor(
b,
pixelsButtons.Color(
prideColors[color][0],
prideColors[color][1],
prideColors[color][2]
)
);
break;
}
}
pixelsSolution.show();
pixelsButtons.show();
delay(WIN_STEP_DELAY_MS);
}
digitalWrite(PIN_SEND_WIN, LOW);
winActive = false;
}
// ------------------ Setup ------------------
void setup() {
randomSeed(analogRead(A0));
Wire.begin();
expander.begin();
pinMode(PIN_TRIGGER_SHUFFLE, INPUT);
pinMode(PIN_POWER_ON, INPUT);
powerOnState = digitalRead(PIN_POWER_ON);
pinMode(PIN_SEND_WIN, OUTPUT);
digitalWrite(PIN_SEND_WIN, LOW);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);
pinMode(8, INPUT_PULLUP);
for (uint8_t i = 0; i < 12; i++)
expander.write(i, HIGH);
pixelsButtons.begin();
pixelsSolution.begin();
generateNewGame();
for (uint8_t i = 0; i < 18; i++)
lastPressed[i] = false;
}
// ------------------ Loop ------------------
void loop() {
if (powerOnState && digitalRead(PIN_POWER_ON) == LOW)
{
powerOnState = false;
pixelsSolution.begin();
for (size_t i = 0; i < 6; i++)
{
pixelsSolution.setPixelColor(i, pixelsSolution.Color(0, 0, 0, 0));
}
pixelsSolution.show();
pixelsButtons.begin();
for (size_t i = 0; i < 18; i++)
{
pixelsButtons.setPixelColor(i, pixelsButtons.Color(0, 0, 0, 0));
}
pixelsButtons.show();
}
// Detecting turn-on
if (!powerOnState && digitalRead(PIN_POWER_ON) == HIGH)
{
generateNewGame();
powerOnState = true;
}
if (!powerOnState)
{
// We are turned-off and stop anything after here
return;
}
unsigned long now = millis();
bool trigger = digitalRead(PIN_TRIGGER_SHUFFLE);
if (trigger && !lastTriggerState) {
generateNewGame();
}
lastTriggerState = trigger;
for (uint8_t i = 0; i < 18; i++) {
bool pressed = readButton(i);
if (pressed && !lastPressed[i] &&
(now - lastPressTime[i] > DEBOUNCE_TIME_MS)) {
lastPressTime[i] = now;
int8_t color = buttonAssignment[i];
if (color >= 0) {
pixelsButtons.setPixelColor(
i,
pixelsButtons.Color(
prideColors[color][0],
prideColors[color][1],
prideColors[color][2]
)
);
pixelsButtons.show();
ledOffTime[i] = now + 1000;
}
if (color < 0 || color != expectedOrder[progressIndex]) {
progressIndex = 0;
} else {
progressIndex++;
if (progressIndex == 6) {
winAnimation();
progressIndex = 0;
}
}
}
if (!winActive && ledOffTime[i] && now >= ledOffTime[i]) {
pixelsButtons.setPixelColor(i, 0);
pixelsButtons.show();
ledOffTime[i] = 0;
}
lastPressed[i] = pressed;
}
}