Загрузка данных



#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo myServo;

#define SERVO_PIN 9
#define JOY_SW 2
#define BUZZER 3

const int GAME_TIME = 50000;
const int HOLD_TIME = 3000;
const int TOLERANCE = 8;

enum State {
  WAITING,
  COUNTDOWN,
  PLAYING,
  FEEDBACK,
  WIN,
  LOSE
};

State state = WAITING;

int roundNumber = 0;
int servoAngle = 0;
int targetAngle = 90;
int direction = 1;

bool holding = false;
unsigned long holdStart = 0;

unsigned long gameStart = 0;
unsigned long stateStart = 0;
unsigned long lastMove = 0;
unsigned long lastMusic = 0;
unsigned long lastLCD = 0;

int musicIndex = 0;

int melody[] = {
  523, 587, 659, 784,
  659, 587, 523, 440
};

// Экран ожидания
void waitingScreen() {
  noTone(BUZZER);

  lcd.clear();
  lcd.print("SERVO CHALLENGE");
  lcd.setCursor(0, 1);
  lcd.print("PRESS TO START");

  myServo.write(90);
  servoAngle = 90;

  state = WAITING;
}

// Начало игры
void startGame() {
  roundNumber = 0;

  lcd.clear();
  lcd.print("GET READY!");

  state = COUNTDOWN;
  stateStart = millis();
}

// Новый раунд
void newRound() {
  targetAngle = random(20, 161);

  // Случайно выбираем направление движения
  servoAngle = random(0, 181);
  direction = random(0, 2) == 0 ? -1 : 1;

  myServo.write(servoAngle);

  holding = false;

  lcd.clear();
  lcd.print("ROUND ");
  lcd.print(roundNumber + 1);
  lcd.print("/5");

  state = PLAYING;
  lastMove = millis();
}

// Музыка
void playMusic() {
  if (millis() - lastMusic >= 250) {
    lastMusic = millis();

    tone(BUZZER, melody[musicIndex], 180);

    musicIndex++;
    if (musicIndex >= 8) {
      musicIndex = 0;
    }
  }
}

// Экран игры
void updateLCD() {
  int difference = targetAngle - servoAngle;

  lcd.setCursor(0, 0);
  lcd.print("TIME:");
  lcd.print((GAME_TIME - (millis() - gameStart)) / 1000);
  lcd.print(" R:");
  lcd.print(roundNumber + 1);
  lcd.print("   ");

  lcd.setCursor(0, 1);

  if (holding) {
    int remaining =
      3 - (millis() - holdStart) / 1000;

    lcd.print("HOLD: ");
    lcd.print(remaining);
    lcd.print(" SEC      ");
  }
  else if (abs(difference) <= TOLERANCE) {
    lcd.print("TARGET: OK!     ");
  }
  else {
    lcd.print("TARGET: ");
    lcd.print(difference > 0 ? "RIGHT -> " : "<- LEFT ");
    lcd.print("   ");
  }
}

// Проверка результата
void checkResult() {
  int difference = abs(targetAngle - servoAngle);

  noTone(BUZZER);
  holding = false;

  if (difference <= TOLERANCE) {
    roundNumber++;

    lcd.clear();
    lcd.print("CORRECT!");

    lcd.setCursor(0, 1);
    lcd.print("SCORE: ");
    lcd.print(roundNumber);
    lcd.print("/5");

    tone(BUZZER, 1300, 200);

    if (roundNumber >= 5) {
      state = WIN;
      lcd.clear();
      lcd.print("YOU WIN!");
      lcd.setCursor(0, 1);
      lcd.print("5/5 COMPLETED");
      myServo.write(90);
    }
    else {
      state = FEEDBACK;
      stateStart = millis();
    }
  }
  else {
    lcd.clear();
    lcd.print("WRONG ANGLE!");
    lcd.setCursor(0, 1);
    lcd.print("TRY AGAIN");

    tone(BUZZER, 250, 200);

    state = FEEDBACK;
    stateStart = millis();
  }
}

// Поражение
void loseGame() {
  state = LOSE;

  holding = false;

  lcd.clear();
  lcd.print("TIME OVER!");
  lcd.setCursor(0, 1);
  lcd.print("GAME OVER");

  tone(BUZZER, 1000);
  stateStart = millis();
}

void setup() {
  pinMode(JOY_SW, INPUT_PULLUP);
  pinMode(BUZZER, OUTPUT);

  myServo.attach(SERVO_PIN);

  lcd.init();
  lcd.backlight();

  randomSeed(analogRead(A2));

  waitingScreen();
}

void loop() {
  unsigned long now = millis();

  // ОЖИДАНИЕ
  if (state == WAITING) {
    if (digitalRead(JOY_SW) == LOW) {
      delay(30);

      if (digitalRead(JOY_SW) == LOW) {
        startGame();

        while (digitalRead(JOY_SW) == LOW) {}
      }
    }
  }

  // ОТСЧЁТ
  else if (state == COUNTDOWN) {
    int count = 3 - (now - stateStart) / 1000;

    if (count >= 1) {
      lcd.setCursor(0, 0);
      lcd.print("STARTING...");

      lcd.setCursor(0, 1);
      lcd.print("       ");
      lcd.setCursor(7, 1);
      lcd.print(count);

      static int lastCount = 0;

      if (count != lastCount) {
        tone(BUZZER, 1000, 180);
        lastCount = count;
      }
    }

    if (now - stateStart >= 3000) {
      noTone(BUZZER);

      gameStart = millis();
      lastMusic = millis();
      musicIndex = 0;

      lastCountReset();
      newRound();
    }
  }

  // ИГРА
  else if (state == PLAYING) {

    if (now - gameStart >= GAME_TIME) {
      loseGame();
      return;
    }

    playMusic();

    // Автоматическое движение серво
    if (!holding && now - lastMove >= 35) {
      lastMove = now;

      servoAngle += direction;

      if (servoAngle >= 180) {
        servoAngle = 180;
        direction = -1;
      }

      if (servoAngle <= 0) {
        servoAngle = 0;
        direction = 1;
      }

      myServo.write(servoAngle);
    }

    // Обновление экрана
    if (now - lastLCD >= 150) {
      lastLCD = now;
      updateLCD();
    }

    // Удержание SEL
    if (digitalRead(JOY_SW) == LOW) {

      if (!holding) {
        holding = true;
        holdStart = now;
      }

      // Серво замерло в момент нажатия
      if (now - holdStart >= HOLD_TIME) {
        checkResult();
      }
    }
    else {
      holding = false;
    }
  }

  // Результат раунда
  else if (state == FEEDBACK) {

    if (now - gameStart >= GAME_TIME) {
      loseGame();
    }
    else if (now - stateStart >= 700) {
      noTone(BUZZER);
      newRound();
    }
  }

  // ПОБЕДА
  else if (state == WIN) {
    if (digitalRead(JOY_SW) == LOW) {
      while (digitalRead(JOY_SW) == LOW) {}
      waitingScreen();
    }
  }

  // ПОРАЖЕНИЕ
  else if (state == LOSE) {
    if (now - stateStart >= 3000) {
      noTone(BUZZER);
      waitingScreen();
    }
  }
}

// Сброс счётчика отсчёта
void lastCountReset() {
  // Используется перед новым запуском
}