#include "dummyLibs.h"
#include "module_setup.h"
Module module; // Объект для работы с параметрами модуля
Device device; // Объект для работы с копией устройства
KTANEBusSlave busSlave(&device, &module, I2C_ADDR); // Объект для работы с KTANEBus как слейв
Colors sequenceToShow[SEQUENCE_LENGTH]; // Последовательность для отображения
Colors sequenceToEnter[SEQUENCE_LENGTH]; // Последовательность для ввода
uint8_t gameRound = 1; // Текущее количество вспышек (раунд игры)
uint8_t gameStep = 0; // Текущий шаг игры (количество правильных нажатий)
bool soundIsOn = false; // Звук включен (активируется при первом нажатии)
bool buttonPressed = false; // Флаг нажатия кнопки
void setup() {
// Конфигурируем входы
pinMode(BLUE_BTN, INPUT_PULLUP);
pinMode(YELLOW_BTN, INPUT_PULLUP);
pinMode(GREEN_BTN, INPUT_PULLUP);
pinMode(RED_BTN, INPUT_PULLUP);
// Конфигурируем выходы
pinMode(BLUE_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(RGB_LED_G, OUTPUT);
pinMode(RGB_LED_R, OUTPUT);
busSlave.begin();
// Генерируем случайную последовательность для индикации
generateRandomSequence();
module.status = RUNNING;
Serial.begin(115200);
}
void loop() {
readInputs();
generateSequenceToEnter();
handleButtons();
handleMistakes();
handleModuleStatus();
playSequence();
writeOutputs();
busSlave.dummyBus();
printDebugInfo();
}
// Читаем состояния кнопок
inline void readInputs() {
if (!digitalRead(BLUE_BTN))
inputs.buttonColor = BLUE;
else if (!digitalRead(YELLOW_BTN))
inputs.buttonColor = YELLOW;
else if (!digitalRead(GREEN_BTN))
inputs.buttonColor = GREEN;
else if (!digitalRead(RED_BTN))
inputs.buttonColor = RED;
else inputs.buttonColor = NO_COLOR;
}
// Генерируем последовательность для ввода
inline void generateSequenceToEnter() {
if (device.mistakesNumber > 2) return; // Выходим, если слишком большое количество ошибок
for (uint8_t i; i < SEQUENCE_LENGTH; i++) {
if (device.serialHasVowels()) { // В номере гласная
sequenceToEnter[i] = hasVowelsTransferArray[device.mistakesNumber][sequenceToShow[i] - 1];
} else { // В номере нет гласных
sequenceToEnter[i] = noVowelsTransferArray[device.mistakesNumber][sequenceToShow[i] - 1];
}
}
}
// Обрабатываем нажатия кнопок
void handleButtons() {
static Colors prevButtonColor = NO_COLOR;
if (inputs.buttonColor && !prevButtonColor && module.status == RUNNING) { // Нажата кнопка и модуль в RUNNING
buttonPressed = true;
if (inputs.buttonColor == sequenceToEnter[gameStep]) { // Кнопка правильная
if (gameStep == SEQUENCE_LENGTH - 1) { // если последний раунд
module.status = SOLVED;
} else if (gameStep == gameRound - 1) { // если номер шага равен номеру раунда
gameStep = 0;
gameRound++;
} else { // если номер шага меньше номера раунда
gameStep++;
}
} else { // Кнопка неправильная
gameStep = 0;
module.mistakesNumber++;
}
}
prevButtonColor = inputs.buttonColor;
}
// Обрабатываем ошибки решения
void handleMistakes() {
uint32_t currentTime = millis();
static uint32_t prevTime = 0;
static uint8_t prevMistakesNumber = 0;
if (module.mistakesNumber > prevMistakesNumber) {
outputs.rgbLedRed = true;
prevTime = currentTime;
}
if (currentTime - prevTime >= MISTAKE_FLASH_DURATION) {
outputs.rgbLedRed = false;
}
prevMistakesNumber = module.mistakesNumber;
}
// Обрабатывем статусы модуля
void handleModuleStatus() {
if (module.status == SOLVED) {
outputs.rgbLedGreen = true;
}
}
// Воспроизводим цветные вспышки из последовательности и соответствующие звуки
// Аргументом указыавем текущее число вспышек для воспроизведения (раунд)
inline void playSequence() {
// Если модуль не в состоянии RUNNING, выключаем индикацию и выходим
if (module.status != RUNNING) {
outputs.ledColor = NO_COLOR;
return;
}
uint32_t currentTime = millis();
static uint32_t prevTime = 0;
static uint8_t sequenceIndex = 0;
static enum : uint8_t {
LOOP_PAUSE,
FLASH,
PAUSE,
BTN_PRESSED,
BTN_PAUSE
} state = LOOP_PAUSE;
// Если была нажата кнопка, то приоритет в отображении у нее
if (buttonPressed) {
soundIsOn = true;
buttonPressed = false;
sequenceIndex = 0;
outputs.ledColor = inputs.buttonColor;
tone(SPEAKER, TONES[inputs.buttonColor], FLASH_DURATION);
state = BTN_PRESSED;
prevTime = currentTime;
}
switch (state) {
case BTN_PRESSED:
if (currentTime - prevTime >= FLASH_DURATION) {
outputs.ledColor = NO_COLOR;
prevTime = currentTime;
state = BTN_PAUSE;
}
break;
case BTN_PAUSE:
if (currentTime - prevTime >= BTN_PAUSE_DURATION) {
outputs.ledColor = sequenceToShow[sequenceIndex];
if (soundIsOn) tone(SPEAKER, TONES[sequenceToShow[sequenceIndex]], FLASH_DURATION);
state = FLASH;
prevTime = currentTime;
}
break;
case LOOP_PAUSE:
if (currentTime - prevTime >= LOOP_PAUSE_DURATION) {
outputs.ledColor = sequenceToShow[sequenceIndex];
if (soundIsOn) tone(SPEAKER, TONES[sequenceToShow[sequenceIndex]], FLASH_DURATION);
state = FLASH;
prevTime = currentTime;
}
break;
case FLASH:
if (currentTime - prevTime >= FLASH_DURATION) {
outputs.ledColor = NO_COLOR;
noTone(SPEAKER);
prevTime = currentTime;
sequenceIndex++;
if (sequenceIndex >= SEQUENCE_LENGTH || sequenceIndex >= gameRound) {
sequenceIndex = 0;
state = LOOP_PAUSE;
} else state = PAUSE;
}
break;
case PAUSE:
if (currentTime - prevTime >= PAUSE_DURATION) {
outputs.ledColor = sequenceToShow[sequenceIndex];
if (soundIsOn) tone(SPEAKER, TONES[sequenceToShow[sequenceIndex]], FLASH_DURATION);
state = FLASH;
prevTime = currentTime;
}
break;
}
}
// Пишем состояния выходов
void writeOutputs() {
digitalWrite(BLUE_LED, outputs.ledColor == BLUE);
digitalWrite(YELLOW_LED, outputs.ledColor == YELLOW);
digitalWrite(GREEN_LED, outputs.ledColor == GREEN);
digitalWrite(RED_LED, outputs.ledColor == RED);
digitalWrite(RGB_LED_G, outputs.rgbLedGreen);
digitalWrite(RGB_LED_R, outputs.rgbLedRed);
}
// Генерируем случайную последовательность вспышек
void generateRandomSequence() {
randomSeed(analogRead(A6) ^ micros());
for (uint8_t i = 0; i < SEQUENCE_LENGTH; i++) {
// Случайный цвет (BLUE ... RED)
sequenceToShow[i] = random(BLUE, RED + 1);
}
}
/*************************************** Debug *******************************************************/
const char* colorsNames[] = { "NC", "blue", "yellow", "green", "red" };
void printDebugInfo() {
uint32_t currentTime = millis();
static uint32_t prevTime = 0;
if (currentTime - prevTime > 3000) {
Serial.print(F("gameRound: "));
Serial.println(gameRound);
Serial.print(F("gameStep: "));
Serial.println(gameStep);
Serial.print(F("mistakes: "));
Serial.println(module.mistakesNumber);
Serial.print(F("module.status: "));
Serial.println(module.status);
Serial.print(F("sequenceToShow: "));
for (uint8_t i = 0; i < SEQUENCE_LENGTH; i++) {
Serial.print(colorsNames[sequenceToShow[i]]);
Serial.print(' ');
}
Serial.println();
Serial.print(F("sequenceToEnter: "));
for (uint8_t i = 0; i < SEQUENCE_LENGTH; i++) {
Serial.print(colorsNames[sequenceToEnter[i]]);
Serial.print(' ');
}
Serial.println();
Serial.println(F("------------------"));
prevTime = currentTime;
}
}