Загрузка данных
#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() {
// Используется перед новым запуском
}