#include <Wire.h>
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <Adafruit_NeoPixel.h>
#include <SPI.h>
MD_MAX72XX::fontType_t numeric7Seg[] PROGMEM = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,0,6,126,255,195,195,255,126,6,0,0,255,255,0,0,6,243,251,219,219,223,206,
6,219,219,219,219,255,126,6,15,31,24,24,255,255,6,223,223,219,219,251,115,6,126,255,219,219,251,122,6,3,3,3,3,255,254,
6,118,255,219,219,255,118,6,78,223,219,219,255,126,2,102,102};
#define BUZZER 7
#define RING_PIN 6
#define STRING_PIN 5
#define BTN_START 11
#define BTN_WIN 12
#define DATA_PIN 8
#define CS_PIN 9
#define CLK_PIN 10
#define LED_COUNT 24
#define LED_COUNT2 8
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES 4
#define SLAVE_1 0x10
#define SLAVE_2 0x11
#define SLAVE_3 0x12
#define SLAVE_4 0x13
const uint8_t slaveAddresses[4] = {SLAVE_1, SLAVE_2, SLAVE_3, SLAVE_4};
MD_Parola P(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
Adafruit_NeoPixel ring(LED_COUNT, RING_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel ledstring(LED_COUNT2, STRING_PIN, NEO_GRB + NEO_KHZ800);
#define COLORE_ROSSO ledstring.Color(255, 0, 0)
#define COLORE_VERDE ledstring.Color(0, 255, 0)
#define COLORE_GIALLO ledstring.Color(255, 255, 0)
#define COLORE_BLU ledstring.Color(0, 0, 255)
#define COLORE_BIANCO ledstring.Color(255, 255, 255)
enum { IDLE, PRESTART, RUNNING, FINISHED, WON };
uint8_t state = IDLE;
unsigned long startMs = 0;
unsigned long frozenRem = 0;
const unsigned long TOTAL_SEC = 3600UL;
unsigned long lastBeepSec = 999;
uint8_t blinkCnt = 0;
unsigned long lastBlinkMs = 0;
int pos = 0;
unsigned long lastStepMs = 0;
const float tail[] = {0.9, 0.8, 0.7, 0.6, 0.4, 0.2};
const int tailLen = 6;
unsigned long lastStartPress = 0, lastWinPress = 0;
const unsigned long DEBOUNCE = 200;
uint8_t Fase = 0;
unsigned long lastI2CCheck = 0;
const unsigned long I2C_INTERVAL = 300;
uint8_t currentSlavePolling = 0;
char buf[9];
void setup() {
Wire.begin();
pinMode(BTN_START, INPUT_PULLUP);
pinMode(BTN_WIN, INPUT_PULLUP);
pinMode(BUZZER, OUTPUT);
spegniTuttiLED();
ring.begin();
ring.clear();
ring.show();
ledstring.begin();
ledstring.clear();
ledstring.show();
P.begin();
P.setFont(numeric7Seg);
P.setTextAlignment(PA_CENTER);
P.print("00:00");
}
void loop() {
TimeManager();
I2CManager();
switch (Fase) {
case 1: accendiLED(1, COLORE_VERDE); break;
case 2: accendiLED(2, COLORE_VERDE); break;
case 3: accendiLED(3, COLORE_GIALLO); break;
case 4: accendiLED(4, COLORE_GIALLO); break;
case 5: accendiLED(5, COLORE_GIALLO); break;
case 6: accendiLED(6, COLORE_ROSSO); break;
case 7: accendiLED(7, COLORE_ROSSO); break;
case 8: accendiLED(8, COLORE_ROSSO); break;
default: spegniTuttiLED(); break;
}
}
void I2CManager() {
unsigned long now = millis();
if (state == FINISHED) { sendToAllSlaves(0x99); return; }
if (state != RUNNING) { if (state == IDLE) sendToAllSlaves(0x00); return; }
if (now - lastI2CCheck < I2C_INTERVAL) return;
lastI2CCheck = now;
uint8_t slaveAddr = slaveAddresses[currentSlavePolling];
Wire.beginTransmission(slaveAddr);
Wire.write(0x02);
Wire.endTransmission();
Wire.requestFrom(slaveAddr, (uint8_t)4);
if (Wire.available() >= 4) {
Wire.read();
uint8_t faseRicevuta = Wire.read();
Wire.read();
Wire.read();
if (faseRicevuta > Fase && faseRicevuta <= 8) Fase = faseRicevuta;
}
currentSlavePolling = (currentSlavePolling + 1) % 4;
}
void sendToAllSlaves(uint8_t cmd) {
for (uint8_t i = 0; i < 4; i++) {
Wire.beginTransmission(slaveAddresses[i]);
Wire.write(cmd);
Wire.endTransmission();
delay(5);
}
}
void TimeManager() {
unsigned long now = millis();
if (!digitalRead(BTN_START) && now - lastStartPress > DEBOUNCE) {
lastStartPress = now;
if (state == IDLE) {
state = PRESTART;
blinkCnt = 0;
lastBlinkMs = 0;
Fase = 0;
spegniTuttiLED();
currentSlavePolling = 0;
}
if (state == RUNNING) {
Fase++; //************* PER DEBUG DA TOGLIERE IN FASE FINALE!!!!
}
}
if (!digitalRead(BTN_WIN) && now - lastWinPress > DEBOUNCE) {
lastWinPress = now;
if (state == RUNNING) {
frozenRem = TOTAL_SEC - (now - startMs) / 1000;
state = WON;
tone(BUZZER, 1200, 800);
ring.clear();
ring.show();
} else if (state == IDLE || state == PRESTART) {
state = IDLE;
ring.clear();
ring.show();
P.print("00:00");
noTone(BUZZER);
sendToAllSlaves(0x00);
}
}
if (state == PRESTART) {
if (now - lastBlinkMs > 500) {
lastBlinkMs = now;
if (blinkCnt % 2 == 0) { P.print("60:00"); tone(BUZZER, 1000, 80); }
else P.print(" ");
if (++blinkCnt >= 10) {
tone(BUZZER, 1400, 300);
animazione(COLORE_GIALLO);
state = RUNNING;
startMs = now;
pos = 0;
lastStepMs = now;
lastBeepSec = 999;
Fase = 1;
}
}
return;
}
if (state == RUNNING) {
unsigned long rem = TOTAL_SEC - (now - startMs) / 1000;
if (rem <= 0) {
state = FINISHED;
tone(BUZZER, 400, 1200);
sendToAllSlaves(0x99);
}
}
if (state == FINISHED) {
if (now % 1000 < 500) P.print("00:00"); else P.print(" ");
} else if (state == WON) {
if (now % 800 < 400) {
sprintf(buf, "%02lu:%02lu", frozenRem / 60, frozenRem % 60);
P.print(buf);
} else P.print(" ");
} else if (state == RUNNING) {
unsigned long rem = TOTAL_SEC - (now - startMs) / 1000;
sprintf(buf, "%02lu:%02lu", rem / 60, rem % 60);
P.print(buf);
}
if (state == FINISHED) {
if (now % 1000 < 500) ring.clear();
else for (int i = 0; i < LED_COUNT; i++) ring.setPixelColor(i, ring.Color(255, 0, 0));
ring.show();
animazione(COLORE_ROSSO);
return;
}
if (state == WON) {
if (now - lastStepMs >= 20) {
animazione(COLORE_VERDE);
lastStepMs = now;
ring.clear();
for (int i = 0; i < tailLen; i++) {
int p1 = (pos - i + LED_COUNT) % LED_COUNT;
int p2 = (pos - i + LED_COUNT / 2) % LED_COUNT;
ring.setPixelColor(p1, 0, 255 * tail[i], 0);
ring.setPixelColor(p2, 0, 255 * tail[i], 0);
}
ring.show();
pos = (pos + 1) % LED_COUNT;
}
return;
}
if (state != RUNNING) return;
unsigned long remSec = TOTAL_SEC - (now - startMs) / 1000;
bool fast = (remSec <= 300);
unsigned long baseMs = fast ? 31 : 63;
if (now - lastStepMs >= baseMs) {
lastStepMs = now;
ring.clear();
uint32_t col;
if (remSec > 1800) col = ring.Color(0, 255, 0);
else if (remSec > 900) col = ring.Color(180, 255, 0);
else if (remSec > 300) col = ring.Color(255, 120, 0);
else col = ring.Color(255, 0, 0);
uint8_t r = col >> 16, g = col >> 8, b = col;
for (int i = 0; i < tailLen; i++) {
int p1 = (pos - i + LED_COUNT) % LED_COUNT;
int p2 = (pos - i + LED_COUNT / 2) % LED_COUNT;
ring.setPixelColor(p1, r * tail[i], g * tail[i], b * tail[i]);
ring.setPixelColor(p2, r * tail[i], g * tail[i], b * tail[i]);
}
ring.show();
pos = (pos + 1) % LED_COUNT;
}
if (remSec <= 60 && remSec != lastBeepSec && remSec > 0) {
lastBeepSec = remSec;
tone(BUZZER, 500, 30);
}
}
void accendiLED(uint8_t num, uint32_t colore) {
if (num >= 1 && num <= 8) {
ledstring.setPixelColor(num - 1, colore);
ledstring.show();
}
}
void spegniTuttiLED() {
ledstring.clear();
ledstring.show();
}
void animazione(uint32_t colore) {
for (uint8_t i = 0; i < LED_COUNT2; i++) {
ledstring.setPixelColor(i, colore);
ledstring.show();
}
for (uint8_t i = 0; i < LED_COUNT2; i++) {
ledstring.setPixelColor(i, 0);
ledstring.show();
}
}