#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "TM1637.h"
// ================== CONFIGURAZIONE SLAVE ==================
#define MY_ADDRESS 0x10 // Slave 1: fasi 1-2
#define PHASE_LOW 1
#define PHASE_HIGH 2
enum { SLAVE_IDLE, SLAVE_ACTIVE, SLAVE_FAIL };
uint8_t slaveState = SLAVE_IDLE;
uint8_t currentPhase = 0;
uint16_t codice = 0;
// ================== PIN ==================
#define PIN_POT_1 A2
#define PIN_POT_2 A3
#define PIN_LED_JOY_GREEN 8
#define PIN_LED_JOY_RED 7
#define PIN_LED_JOY_F1 12
#define PIN_LED_JOY_F2 11
#define PIN_LED_JOY_F3 10
#define PIN_LED_JOY_F4 9
#define PIN_BUZZ 6
#define PIN_JOY_1_V A0
#define PIN_JOY_1_H A1
#define PIN_JOY_1_B 5
#define PIN_JOY_2_V A6
#define PIN_JOY_2_H A7
#define PIN_JOY_2_B 4
#define PIN_4DIG_1_DIO 3
#define PIN_4DIG_1_CLK 2
// ================== OGGETTI ==================
LiquidCrystal_I2C lcd(0x27, 16, 2);
TM1637 d1(PIN_4DIG_1_CLK, PIN_4DIG_1_DIO);
// ================== VARIABILI GIOCO ==================
int Cifre[16];
int Codice[4];
bool Ch1 = false, Ch2 = false, Ch3 = false, Ch4 = false;
int PosDisplay[5] = {99, 0, 4, 8, 12};
int VAL_M = 0, VAL_C = 0, VAL_D = 0, VAL_U = 0;
int Passo = 1; // 1..4 (indice logico)
bool FInit = false;
int gameState = 1; // 1 = Fase 1, 2 = Fase 2, 3 = Completato
// Combinazioni casuali Fase 1
const int MIN_DISTANCE = 5;
int Sequence[4][2];
// Ordine casuale delle 4 mappe joystick
int mappaOrder[4] = {1, 2, 3, 4}; // Sarà mescolato all'avvio della fase 2
void setup() {
Wire.begin(MY_ADDRESS);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
initPins();
lcd.init();
lcd.backlight();
d1.init();
d1.set(BRIGHT_TYPICAL);
resetAllOutputs();
}
void loop() {
if (slaveState != SLAVE_ACTIVE) {
delay(50);
return;
}
switch (gameState) {
case 1: fase1(); break;
case 2: fase2(); break;
case 3: /* Completato */ break;
}
delay(10);
}
void receiveEvent(int bytes) {
if (Wire.available() == 0) return;
uint8_t cmd = Wire.read();
switch (cmd) {
case 0x00: // RESET
slaveState = SLAVE_IDLE;
currentPhase = 0;
codice = 0;
gameState = 1;
resetAllOutputs();
break;
case 0x99: // FAIL
slaveState = SLAVE_FAIL;
currentPhase = 0;
failEffects();
break;
default: // ATTIVA
if (slaveState != SLAVE_FAIL) {
slaveState = SLAVE_ACTIVE;
currentPhase = PHASE_LOW;
gameState = 1;
startPhaseLow();
}
break;
}
}
void requestEvent() {
uint8_t response[4];
response[0] = slaveState;
response[1] = currentPhase;
response[2] = highByte(codice);
response[3] = lowByte(codice);
Wire.write(response, 4);
}
// ================== GENERAZIONE COMBINAZIONI FASE 1 ==================
void generaCombinazioniFase1() {
bool used[17] = {false};
for (int i = 0; i < 4; i++) {
int p1, p2;
bool valid = false;
while (!valid) {
p1 = random(1, 17);
if (used[p1]) continue;
do {
p2 = random(1, 17);
} while (abs(p1 - p2) < MIN_DISTANCE || used[p2]);
valid = true;
}
Sequence[i][0] = p1;
Sequence[i][1] = p2;
used[p1] = true;
used[p2] = true;
}
// Mescola ordine di risoluzione
for (int i = 3; i > 0; i--) {
int j = random(0, i + 1);
int temp0 = Sequence[i][0];
int temp1 = Sequence[i][1];
Sequence[i][0] = Sequence[j][0];
Sequence[i][1] = Sequence[j][1];
Sequence[j][0] = temp0;
Sequence[j][1] = temp1;
}
}
// ================== MESCOLA ORDINE MAPPE JOYSTICK ==================
void mescolaMappeJoystick() {
for (int i = 3; i > 0; i--) {
int j = random(0, i + 1);
int temp = mappaOrder[i];
mappaOrder[i] = mappaOrder[j];
mappaOrder[j] = temp;
}
}
// ================== FASE 1 ==================
void fase1() {
char S_A[17] = " ";
char S_B[17] = " ";
int P1 = map(readStablePot(PIN_POT_1), 0, 1023, 1, 16);
int P2 = map(readStablePot(PIN_POT_2), 0, 1023, 1, 16);
for (int i = 1; i <= 16; i++) {
S_A[i-1] = (i == P1) ? '|' : ' ';
S_B[i-1] = (i == P2) ? '|' : ' ';
}
lcd.setCursor(0, 0); lcd.print(S_A);
lcd.setCursor(0, 1); lcd.print(S_B);
if (checkConditions(P1, P2)) {
digitalWrite(PIN_LED_JOY_GREEN, HIGH);
playTone(2000, 300);
delay(1000);
currentPhase = PHASE_HIGH;
gameState = 2;
startPhaseHigh();
}
}
bool checkConditions(int P1, int P2) {
bool* flags[] = {&Ch1, &Ch2, &Ch3, &Ch4};
int leds[] = {PIN_LED_JOY_F1, PIN_LED_JOY_F2, PIN_LED_JOY_F3, PIN_LED_JOY_F4};
for (int i = 0; i < 4; i++) {
if (P1 == Sequence[i][0] && P2 == Sequence[i][1] && !(*flags[i])) {
digitalWrite(leds[i], HIGH);
playTone(1500, 100);
*flags[i] = true;
}
}
return (Ch1 && Ch2 && Ch3 && Ch4);
}
// ================== FASE 2 ==================
void fase2() {
if (!FInit) {
lcd.clear();
lcd.setCursor(0, 0); lcd.print(" GENERAZIONE ");
lcd.setCursor(0, 1); lcd.print(" CODICE ACCESSO ");
delay(2000);
lcd.clear();
lcd.setCursor(0, 0); lcd.print("DECODIFICA DATI ");
for (int i = 0; i < 16; i++) {
lcd.setCursor(i, 1); lcd.print("*"); delay(150);
}
lcd.clear();
GeneraCodici();
mescolaMappeJoystick(); // <--- ORDINE MAPPE RANDOM OGNI PARTITA
playTone(1000, 200);
for (int i = 0; i < 16; i++) {
lcd.setCursor(i, 0); lcd.print(Cifre[i]);
}
lcd.setCursor(0, 1); lcd.print("0000");
digitalWrite(PIN_LED_JOY_GREEN, LOW);
digitalWrite(PIN_LED_JOY_RED, HIGH);
FInit = true;
}
int H1 = analogRead(PIN_JOY_1_H);
int V1 = analogRead(PIN_JOY_1_V);
int H2 = analogRead(PIN_JOY_2_H);
int V2 = analogRead(PIN_JOY_2_V);
int mappaAttuale = mappaOrder[Passo - 1];
switch (mappaAttuale) {
case 1:
if (V1 < 300) VAL_U++; else if (V1 > 700) VAL_C--;
if (H1 > 700) VAL_C++; else if (H1 < 300) VAL_D--;
if (V2 < 300) VAL_M++; else if (V2 > 700) VAL_U--;
if (H2 > 700) VAL_D++; else if (H2 < 300) VAL_M--;
break;
case 2:
if (V1 < 300) VAL_D--; else if (V1 > 700) VAL_C--;
if (H1 > 700) VAL_U++; else if (H1 < 300) VAL_M--;
if (V2 < 300) VAL_D++; else if (V2 > 700) VAL_C++;
if (H2 > 700) VAL_M++; else if (H2 < 300) VAL_U--;
break;
case 3:
if (V1 < 300) VAL_U--; else if (V1 > 700) VAL_D--;
if (H1 > 700) VAL_M++; else if (H1 < 300) VAL_C--;
if (V2 < 300) VAL_D++; else if (V2 > 700) VAL_M--;
if (H2 > 700) VAL_C++; else if (H2 < 300) VAL_U++;
break;
case 4:
if (V1 < 300) VAL_D++; else if (V1 > 700) VAL_U--;
if (H1 > 700) VAL_C++; else if (H1 < 300) VAL_M--;
if (V2 < 300) VAL_U++; else if (V2 > 700) VAL_D--;
if (H2 > 700) VAL_M++; else if (H2 < 300) VAL_C--;
break;
}
VAL_M = constrain(VAL_M, 0, 9);
VAL_C = constrain(VAL_C, 0, 9);
VAL_D = constrain(VAL_D, 0, 9);
VAL_U = constrain(VAL_U, 0, 9);
d1.display(0, VAL_M);
d1.display(1, VAL_C);
d1.display(2, VAL_D);
d1.display(3, VAL_U);
if (digitalRead(PIN_JOY_1_B) == LOW) {
delay(200); // debounce
if (checkOK(Passo, VAL_M, VAL_C, VAL_D, VAL_U)) {
Passo++;
playTone(2000, 200);
if (Passo == 5) {
// FASE 2 COMPLETATA
digitalWrite(PIN_LED_JOY_F1, HIGH);
digitalWrite(PIN_LED_JOY_F2, HIGH);
digitalWrite(PIN_LED_JOY_F3, HIGH);
digitalWrite(PIN_LED_JOY_F4, HIGH);
digitalWrite(PIN_LED_JOY_GREEN, HIGH);
digitalWrite(PIN_LED_JOY_RED, LOW);
d1.display(0, Codice[0]);
d1.display(1, Codice[1]);
d1.display(2, Codice[2]);
d1.display(3, Codice[3]);
lcd.clear();
lcd.setCursor(0, 0); lcd.print(" AUTORIZZATO AL ");
lcd.setCursor(0, 1); lcd.print(" CODICE SBLOCCO ");
delay(3000);
lcd.noBacklight();
currentPhase = 3; // Segnala al Master passaggio alla fase 3
gameState = 3;
playTone(3000, 500);
} else {
digitalWrite(PIN_LED_JOY_F1 + (Passo - 2), HIGH);
}
} else {
playTone(100, 300);
lcd.setCursor(PosDisplay[Passo], 1); lcd.print("0000");
VAL_M = VAL_C = VAL_D = VAL_U = 0;
d1.clearDisplay();
}
}
}
bool checkOK(int B, int m, int c, int d, int u) {
int pos = PosDisplay[B];
if (Cifre[pos] == m &&
Cifre[pos+1] == c &&
Cifre[pos+2] == d &&
Cifre[pos+3] == u) {
lcd.setCursor(pos, 1); lcd.print(m); lcd.print(c); lcd.print(d); lcd.print(u);
return true;
}
return false;
}
// ================== FUNZIONI DI SUPPORTO ==================
void initPins() {
pinMode(PIN_POT_1, INPUT);
pinMode(PIN_POT_2, INPUT);
pinMode(PIN_LED_JOY_GREEN, OUTPUT);
pinMode(PIN_LED_JOY_RED, OUTPUT);
pinMode(PIN_LED_JOY_F1, OUTPUT);
pinMode(PIN_LED_JOY_F2, OUTPUT);
pinMode(PIN_LED_JOY_F3, OUTPUT);
pinMode(PIN_LED_JOY_F4, OUTPUT);
pinMode(PIN_BUZZ, OUTPUT);
pinMode(PIN_JOY_1_V, INPUT);
pinMode(PIN_JOY_1_H, INPUT);
pinMode(PIN_JOY_1_B, INPUT_PULLUP);
pinMode(PIN_JOY_2_V, INPUT);
pinMode(PIN_JOY_2_H, INPUT);
pinMode(PIN_JOY_2_B, INPUT_PULLUP);
}
int readStablePot(int pin) {
long sum = 0;
for (int i = 0; i < 10; i++) {
sum += analogRead(pin);
delay(2);
}
return sum / 10;
}
void GeneraCodici() {
long seed = analogRead(A0) + analogRead(A7) + micros();
randomSeed(seed);
for (int i = 0; i < 16; i++) {
int n;
do { n = random(10); } while (i > 0 && n == Cifre[i-1]);
Cifre[i] = n;
}
for (int i = 0; i < 4; i++) {
bool ok;
do {
ok = true;
Codice[i] = random(10);
for (int j = 0; j < i; j++) if (Codice[i] == Codice[j]) ok = false;
} while (!ok);
}
}
void startPhaseLow() {
randomSeed(micros() + analogRead(A0));
generaCombinazioniFase1();
Ch1 = Ch2 = Ch3 = Ch4 = false;
digitalWrite(PIN_LED_JOY_F1, LOW);
digitalWrite(PIN_LED_JOY_F2, LOW);
digitalWrite(PIN_LED_JOY_F3, LOW);
digitalWrite(PIN_LED_JOY_F4, LOW);
digitalWrite(PIN_LED_JOY_RED, HIGH);
digitalWrite(PIN_LED_JOY_GREEN, LOW);
lcd.backlight();
lcd.clear();
}
void startPhaseHigh() {
FInit = false;
Passo = 1;
VAL_M = VAL_C = VAL_D = VAL_U = 0;
d1.clearDisplay();
lcd.backlight();
lcd.clear();
}
void resetAllOutputs() {
digitalWrite(PIN_LED_JOY_GREEN, LOW);
digitalWrite(PIN_LED_JOY_RED, LOW);
digitalWrite(PIN_LED_JOY_F1, LOW);
digitalWrite(PIN_LED_JOY_F2, LOW);
digitalWrite(PIN_LED_JOY_F3, LOW);
digitalWrite(PIN_LED_JOY_F4, LOW);
d1.clearDisplay();
lcd.clear();
lcd.noBacklight();
noTone(PIN_BUZZ);
gameState = 1;
FInit = false;
Passo = 1;
}
void failEffects() {
resetAllOutputs();
for (int i = 0; i < 5; i++) {
digitalWrite(PIN_LED_JOY_RED, HIGH);
tone(PIN_BUZZ, 200, 300);
delay(400);
digitalWrite(PIN_LED_JOY_RED, LOW);
delay(300);
}
}
void playTone(int freq, int dur) {
tone(PIN_BUZZ, freq);
delay(dur);
noTone(PIN_BUZZ);
}