//Định nghĩa các thông số liên quan đến dự án Blynk.
//Đây là thông tin cần thiết để kết nối ESP32 của bạn với dự án Blynk cụ thể*/
#define BLYNK_TEMPLATE_ID "TMPL6lFoIEDfW"
#define BLYNK_TEMPLATE_NAME "First Project"
#define BLYNK_AUTH_TOKEN "MTjPMqblBfPmw0EcyVv1SZ6QsIdBdlYc"
//Định nghĩa các thư viện cần sử dụng (sound, Wifi, Blynk)
#include "pitches.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
/*Định nghĩa các cổng kết nối cho LED và buzzer với ESP32*/
const uint8_t ledPins[] = {8, 7, 6, 5};
#define SPEAKER_PIN 10
// Định nghĩa các cổng kết nối với LED 8 thanh của ESP32
const int LATCH_PIN = 18; // 74HC595 pin 12
const int DATA_PIN = 19; // 74HC595 pin 14
const int CLOCK_PIN = 9; // 74HC595 pin 11
// Định nghĩa các biến sử dụng trong chương trình
int actualButton = -1;
bool buttonPressed = false;
// Định nghĩa độ lớn mảng (level của game)
#define MAX_GAME_LENGTH 100
// Định nghĩa âm thanh cho buzzer
const int gameTones[] = { NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5};
// Định nghĩa mảng lưu giá trị game và chỉ mục của từng level
uint8_t gameSequence[MAX_GAME_LENGTH] = {0};
uint8_t gameIndex = 0;
//Định nghĩa tên và mật khẩu của mạng Wi-Fi mà ESP32 sẽ kết nối.
char ssid[] = "Wokwi-GUEST";
char pass[] = "";
//Đây là Auth Token, được sử dụng để xác thực thiết bị với dự án Blynk cụ thể.
char auth[] = BLYNK_AUTH_TOKEN;
//Trong hàm setup(), chương trình bắt đầu bằng việc thiết lập kết nối với ứng dụng Blynk,
//khai báo các chân GPIO
void setup() {
//Định nghĩa tốc độ truyền dữ liệu giữa ESP32 và máy tính để truyền nhân dữ liệu
Serial.begin(9600);
//Kết nối blynk thông qua tên và password wifi kèm với token để định nghĩa device trên Blynk
Blynk.begin(auth, ssid, pass);
//Khai báo trạng thái cho các chân GPIO của ESP32
for (byte i = 0; i < 4; i++) {
pinMode(ledPins[i], OUTPUT);
}
pinMode(SPEAKER_PIN, OUTPUT);
pinMode(LATCH_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(DATA_PIN, OUTPUT);
// The following line primes the random number generator.
// It assumes pin 4 is floating (disconnected):
randomSeed(analogRead(4));
}
/* Cấu hình cho LED 7 đoạn */
const uint8_t digitTable[] = {
0b11000000,
0b11111001,
0b10100100,
0b10110000,
0b10011001,
0b10010010,
0b10000010,
0b11111000,
0b10000000,
0b10010000,
};
const uint8_t DASH = 0b10111111;
// Hàm gửi dữ liệu cho LED 7 đoạn
void sendScore(uint8_t high, uint8_t low) {
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, low);
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, high);
digitalWrite(LATCH_PIN, HIGH);
}
// Hàm hiển thị dữ liệu (điểm) cho LED 7 đoạn
void displayScore() {
int high = gameIndex % 100 / 10;
int low = gameIndex % 10;
sendScore(high ? digitTable[high] : 0xff, digitTable[low]);
}
// Hàm thay đổi trạng thái led và âm thanh buzzer
void lightLedAndPlayTone(byte ledIndex) {
digitalWrite(ledPins[ledIndex], HIGH);
tone(SPEAKER_PIN, gameTones[ledIndex]);
delay(300);
digitalWrite(ledPins[ledIndex], LOW);
noTone(SPEAKER_PIN);
}
/**
Phát chuỗi nốt nhạc hiện tại mà người dùng phải lặp lại theo tín hiệu đèn LED
*/
void playSequence() {
for (int i = 0; i < gameIndex; i++) {
byte currentLed = gameSequence[i];
lightLedAndPlayTone(currentLed);
delay(50);
}
}
//Đây là các hàm callback được gọi khi giá trị nút nhấn trên ứng dụng Blynk thay đổi.
//Nếu giá trị của nút nhấn là 1 (được bật), thì thay đổi trạng thái và giá trị các biến,
//để ESP32 xử lý
BLYNK_WRITE(V0)
{
int redBtn = param.asInt();
actualButton = 2;
if (redBtn == 1)
{
buttonPressed = true;
}
}
BLYNK_WRITE(V1)
{
int greenBtn = param.asInt();
actualButton = 3;
if (greenBtn == 1)
{
buttonPressed = true;
}
}
BLYNK_WRITE(V2)
{
int blueBtn = param.asInt();
actualButton = 1;
if (blueBtn == 1)
{
buttonPressed = true;
}
}
BLYNK_WRITE(V3)
{
int yellowBtn = param.asInt();
actualButton = 0;
if (yellowBtn == 1)
{
buttonPressed = true;
}
}
/**
Thông báo khi thua
*/
void gameOver() {
Serial.print("Game over! your score: ");
Serial.println(gameIndex - 1);
gameIndex = 0;
delay(200);
// Play a Wah-Wah-Wah-Wah sound
tone(SPEAKER_PIN, NOTE_DS5);
delay(300);
tone(SPEAKER_PIN, NOTE_D5);
delay(300);
tone(SPEAKER_PIN, NOTE_CS5);
delay(300);
for (byte i = 0; i < 10; i++) {
for (int pitch = -10; pitch <= 10; pitch++) {
tone(SPEAKER_PIN, NOTE_C5 + pitch);
delay(6);
}
}
noTone(SPEAKER_PIN);
sendScore(DASH, DASH);
delay(500);
}
/**
Phát chuỗi nhạc khi qua màn
*/
void playLevelUpSound() {
tone(SPEAKER_PIN, NOTE_E4);
delay(150);
tone(SPEAKER_PIN, NOTE_G4);
delay(150);
tone(SPEAKER_PIN, NOTE_E5);
delay(150);
tone(SPEAKER_PIN, NOTE_C5);
delay(150);
tone(SPEAKER_PIN, NOTE_D5);
delay(150);
tone(SPEAKER_PIN, NOTE_G5);
delay(150);
noTone(SPEAKER_PIN);
}
/**
Hàm main chính
*/
void loop() {
displayScore();
// Thêm 1 số ngẫu nhiên từ 1 - 4 vào cuối chuỗi game để tăng độ khó
// Người dùng sẽ lặp lại các nút nhấn dựa theo màu trong chuỗi game
gameSequence[gameIndex] = random(0, 4);
// Tăng level
gameIndex++;
if (gameIndex >= MAX_GAME_LENGTH) {
gameIndex = MAX_GAME_LENGTH - 1;
}
// Bắt đầu game
bool isGameOver = false;
// Hiển thị màu led theo thứ tự trong chuỗi game
playSequence();
// Vòng lặp để kiểm tra từng nút nhấn của người chơi
for (int i = 0; i < gameIndex; i++) {
// Lấy giá trị nút nhấn mà chương trình mong muốn người dùng nhấn
byte expectedButton = gameSequence[i];
// Kiểm tra trạng thái button (Nhấn hay chưa). Nếu chưa thì chờ đến khi người dùng nhấn
while (!buttonPressed)
{
// Chạy các hàm callback BLYNK_WRITE
Blynk.run();
// Kiểm tra nếu người dùng đã nhấn và giá trị button nhấn
if (actualButton > -1 && buttonPressed)
{
// Hiển thị đèn led tương ứng với màu button người dùng nhấn
lightLedAndPlayTone(actualButton);
// Nếu người dùng nhấn nút khác với mong muốn thì game over
if (expectedButton != actualButton) {
// Chơi nhạc và reset level + điểm
gameOver();
isGameOver = true;
}
}
}
// Kiểm tra nếu đã game over thì dừng vòng lặp để kiểm tra các nút nhấn
// kế tiếp
if (isGameOver)
{
break;
}
// Đặt lại trạng thái người dùng chưa nhấn để vòng lặp for tiếp tục
buttonPressed = false;
actualButton = -1;
}
// Delay 0.3s để người dùng thở chờ game kế tiếp
delay(300);
// Nếu level > 0 thì level up (Tức là người dùng đã nhập đúng và
// có thể qua màn kế)
if (gameIndex > 0) {
playLevelUpSound();
delay(300);
}
}