Код (main.cpp)
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
const int joyX = 0;
const int joyY = 1;
const int joySW = 2;
// ← РАЗМЕР ПОЛЯ (можно увеличить FIELD_H до 16, но уменьшить CELL_SIZE до 4)
#define FIELD_W 10
#define FIELD_H 12
#define CELL_SIZE 5 // ← РАЗМЕР КЛЕТКИ (4 — мельче, 6 — крупнее)
#define FIELD_X 10 // ← ПОЗИЦИЯ ПОЛЯ ПО X
#define FIELD_Y 2 // ← ПОЗИЦИЯ ПОЛЯ ПО Y
int field[FIELD_H][FIELD_W] = {0};
// Фигуры
const int figures[7][4][4] = {
{{0,1,0,0},{0,1,0,0},{0,1,0,0},{0,1,0,0}},
{{0,1,1,0},{0,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{0,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,1,1,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,0,1,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}}
};
int currentFig[4][4];
int figX = 3, figY = 0;
int score = 0, highScore = 0;
bool gameOver = false;
unsigned long lastDrop = 0;
int dropSpeed = 800; // ← НАЧАЛЬНАЯ СКОРОСТЬ (400 — быстро, 1200 — медленно)
void spawnFigure() {
int figType = random(0, 7);
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
currentFig[i][j] = figures[figType][i][j];
figX = 3;
figY = 0;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (currentFig[i][j] && field[figY + i][figX + j])
gameOver = true;
}
// ← ПОВОРОТ ФИГУРЫ (если неудобно кнопкой SW, можно заменить на джойстик вверх)
void rotateFigure() {
int temp[4][4];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
temp[j][3 - i] = currentFig[i][j];
bool canRotate = true;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (temp[i][j])
if (figX + j < 0 figX + j >= FIELD_W figY + i >= FIELD_H || field[figY + i][figX + j])
canRotate = false;
if (canRotate) {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
currentFig[i][j] = temp[i][j];
}
}
bool canMove(int dx, int dy) {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (currentFig[i][j]) {
int nx = figX + j + dx;
int ny = figY + i + dy;
if (nx < 0 nx >= FIELD_W ny >= FIELD_H) return false;
if (ny >= 0 && field[ny][nx]) return false;
}
return true;
}
void placeFigure() {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (currentFig[i][j])
field[figY + i][figX + j] = 1;
for (int i = FIELD_H - 1; i >= 0; i--) {
bool full = true;
for (int j = 0; j < FIELD_W; j++)
if (!field[i][j]) full = false;
if (full) {
for (int k = i; k > 0; k--)
for (int j = 0; j < FIELD_W; j++)
field[k][j] = field[k - 1][j];
for (int j = 0; j < FIELD_W; j++) field[0][j] = 0;
score += 100; // ← ОЧКИ ЗА ЛИНИЮ (можно 50, 200)
if (dropSpeed > 200) dropSpeed -= 50; // ← УСКОРЕНИЕ ЗА ЛИНИЮ (50 — плавно, 100 — резко)
i++;
}
}
spawnFigure();
}
void setup() {
Wire.begin(5, 6);
pinMode(joySW, INPUT_PULLUP);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.display();
randomSeed(analogRead(0));
for (int i = 0; i < FIELD_H; i++)
for (int j = 0; j < FIELD_W; j++)
field[i][j] = 0;
spawnFigure();
}
void loop() {
if (gameOver) {
if (score > highScore) highScore = score;
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(15, 10);
display.println("GAME");
display.setCursor(25, 30);
display.println("OVER");
display.setTextSize(1);
🧱 ДЕНЬ 19: ТЕТРИС НА ESP32
Привет, это Вадим! 👋
Сегодня на YouTube вышел девятнадцатый урок. Делаем легендарную игру — Тетрис на ESP32! Фигуры падают, поворачиваются, собираем линии! 🧱🎮
---
📋 СОДЕРЖАНИЕ:
1️⃣ Подключение джойстика и дисплея
2️⃣ Полный код игры
3️⃣ Игровой процесс — рекорды!
---
🛠 ЧТО НУЖНО:
🔹 Плата ESP32-C3
🔹 Джойстик KY-023
🔹 OLED-дисплей 0.96 дюйма, I2C, SSD1306
🔹 Провода мама-мама (7 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
OLED-дисплей:
Дисплей ESP32-C3
VCC 3.3V
GND GND
SCL GPIO 6
SDA GPIO 5
Джойстик KY-023:
Джойстик ESP32-C3
+5V 3.3V
GND GND
VRx GPIO 0
VRy GPIO 1
SW GPIO 2
---
📄 platformio.ini:
---
💻 КОД (main.cpp):
Код закреплён в первом комментарии под этим постом 👇
---
⚙️ НАСТРОЙКА ТЕТРИСА:
В коде есть параметры, которые можно изменить под себя:
1. Скорость падения:
Найди строку:
Чем меньше число, тем быстрее падают фигуры. 400 — быстро, 1200 — медленно.
2. Ускорение за линию:
Найди строку:
Число 50 — на сколько ускоряется игра за каждую собранную линию. 100 — резкое ускорение, 20 — плавное.
3. Очки за линию:
Найди строку:
Можно поменять на 200 или 50 — сколько очков даётся за линию.
4. Размер поля:
Найди строки:
Можно сделать поле шире (12) или выше (16). Но тогда нужно подбирать CELL_SIZE и FIELD_X.
5. Кнопка поворота:
По умолчанию фигура поворачивается по кнопке SW. Можно заменить на джойстик вверх — поменяй условие if (sw && !lastSW) на if (yVal < 1000 && !lastUp).
---
📺 СМОТРЕТЬ ВИДЕОУРОК:
👉 https://youtube.com/shorts/VkjQaQ8rlFI?si=nY9wk_2szBEHxfFS
---
📅 СЛЕДУЮЩИЙ УРОК:
Через 1–2 дня — Тетрис со звуком! Добавим зуммер! 🔔🧱
Не пропусти! 🔔
---
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
Привет, это Вадим! 👋
Сегодня на YouTube вышел девятнадцатый урок. Делаем легендарную игру — Тетрис на ESP32! Фигуры падают, поворачиваются, собираем линии! 🧱🎮
---
📋 СОДЕРЖАНИЕ:
1️⃣ Подключение джойстика и дисплея
2️⃣ Полный код игры
3️⃣ Игровой процесс — рекорды!
---
🛠 ЧТО НУЖНО:
🔹 Плата ESP32-C3
🔹 Джойстик KY-023
🔹 OLED-дисплей 0.96 дюйма, I2C, SSD1306
🔹 Провода мама-мама (7 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
OLED-дисплей:
Дисплей ESP32-C3
VCC 3.3V
GND GND
SCL GPIO 6
SDA GPIO 5
Джойстик KY-023:
Джойстик ESP32-C3
+5V 3.3V
GND GND
VRx GPIO 0
VRy GPIO 1
SW GPIO 2
---
📄 platformio.ini:
[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
build_flags =
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
adafruit/Adafruit GFX Library@^1.11.11
adafruit/Adafruit SSD1306@^2.5.13
---
💻 КОД (main.cpp):
Код закреплён в первом комментарии под этим постом 👇
---
⚙️ НАСТРОЙКА ТЕТРИСА:
В коде есть параметры, которые можно изменить под себя:
1. Скорость падения:
Найди строку:
int dropSpeed = 800;
Чем меньше число, тем быстрее падают фигуры. 400 — быстро, 1200 — медленно.
2. Ускорение за линию:
Найди строку:
if (dropSpeed > 200) dropSpeed -= 50;
Число 50 — на сколько ускоряется игра за каждую собранную линию. 100 — резкое ускорение, 20 — плавное.
3. Очки за линию:
Найди строку:
score += 100;
Можно поменять на 200 или 50 — сколько очков даётся за линию.
4. Размер поля:
Найди строки:
#define FIELD_W 10
#define FIELD_H 12
Можно сделать поле шире (12) или выше (16). Но тогда нужно подбирать CELL_SIZE и FIELD_X.
5. Кнопка поворота:
По умолчанию фигура поворачивается по кнопке SW. Можно заменить на джойстик вверх — поменяй условие if (sw && !lastSW) на if (yVal < 1000 && !lastUp).
---
📺 СМОТРЕТЬ ВИДЕОУРОК:
👉 https://youtube.com/shorts/VkjQaQ8rlFI?si=nY9wk_2szBEHxfFS
---
📅 СЛЕДУЮЩИЙ УРОК:
Через 1–2 дня — Тетрис со звуком! Добавим зуммер! 🔔🧱
Не пропусти! 🔔
---
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
Telegram
ESP: от новичка до профи
🎓 Уроки для новичков: просто, понятно, без сложных слов.
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
display.setCursor(10, 50);
display.print("S:"); display.print(score);
display.setCursor(60, 50);
display.print("R:"); display.print(highScore);
display.display();
if (digitalRead(joySW) == LOW) {
gameOver = false; score = 0; dropSpeed = 800;
for (int i = 0; i < FIELD_H; i++)
for (int j = 0; j < FIELD_W; j++)
field[i][j] = 0;
spawnFigure();
delay(300);
}
return;
}
static bool lastLeft = false, lastRight = false, lastDown = false, lastSW = false;
int xVal = analogRead(joyX);
int yVal = analogRead(joyY);
bool sw = (digitalRead(joySW) == LOW);
// ← УПРАВЛЕНИЕ ВЛЕВО (можно поменять порог 1000 на 800 или 1200)
if (xVal < 1000 && !lastLeft) {
if (canMove(-1, 0)) figX--;
lastLeft = true;
}
if (xVal >= 1000) lastLeft = false;
// ← УПРАВЛЕНИЕ ВПРАВО
if (xVal > 3000 && !lastRight) {
if (canMove(1, 0)) figX++;
lastRight = true;
}
if (xVal <= 3000) lastRight = false;
// ← УСКОРЕНИЕ ВНИЗ (джойстик вниз)
if (yVal > 3000 && !lastDown) {
if (canMove(0, 1)) figY++;
lastDown = true;
}
if (yVal <= 3000) lastDown = false;
// ← ПОВОРОТ КНОПКОЙ SW (можно заменить на yVal < 1000 — поворот вверх)
if (sw && !lastSW) {
rotateFigure();
lastSW = true;
}
if (!sw) lastSW = false;
// ← АВТОПАДЕНИЕ (50 — скорость при удержании вниз)
if (millis() - lastDrop > (yVal > 3000 ? 50 : dropSpeed)) {
if (canMove(0, 1)) {
figY++;
} else {
placeFigure();
}
lastDrop = millis();
}
display.clearDisplay();
// ← ПОЛЕ (можно убрать рамку — закомментируй следующую строку)
display.drawRect(FIELD_X - 1, FIELD_Y - 1, FIELD_W * CELL_SIZE + 2, FIELD_H * CELL_SIZE + 2, SSD1306_WHITE);
for (int i = 0; i < FIELD_H; i++)
for (int j = 0; j < FIELD_W; j++)
if (field[i][j])
display.fillRect(FIELD_X + j * CELL_SIZE, FIELD_Y + i * CELL_SIZE, CELL_SIZE, CELL_SIZE, SSD1306_WHITE);
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (currentFig[i][j])
display.fillRect(FIELD_X + (figX + j) * CELL_SIZE, FIELD_Y + (figY + i) * CELL_SIZE, CELL_SIZE, CELL_SIZE, SSD1306_WHITE);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("S:"); display.print(score);
display.setCursor(80, 0);
display.print("R:"); display.print(highScore);
display.display();
delay(30);
}
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
const int joyX = 0;
const int joyY = 1;
const int joySW = 2;
const int buzzerPin = 7; // ← ПИН ЗУММЕРА (можно поменять на другой)
// ← РАЗМЕР ПОЛЯ
#define FIELD_W 10
#define FIELD_H 12
#define CELL_SIZE 5 // ← РАЗМЕР КЛЕТКИ (4 — мельче, 6 — крупнее)
#define FIELD_X 10 // ← ПОЗИЦИЯ ПОЛЯ ПО X
#define FIELD_Y 2 // ← ПОЗИЦИЯ ПОЛЯ ПО Y
int field[FIELD_H][FIELD_W] = {0};
// Фигуры
const int figures[7][4][4] = {
{{0,1,0,0},{0,1,0,0},{0,1,0,0},{0,1,0,0}},
{{0,1,1,0},{0,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{0,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,1,1,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{1,0,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
{{0,0,1,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}}
};
int currentFig[4][4];
int figX = 3, figY = 0;
int score = 0, highScore = 0;
bool gameOver = false;
unsigned long lastDrop = 0;
int dropSpeed = 800; // ← НАЧАЛЬНАЯ СКОРОСТЬ (400 — быстро, 1200 — медленно)
void spawnFigure() {
int figType = random(0, 7);
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
currentFig[i][j] = figures[figType][i][j];
figX = 3;
figY = 0;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (currentFig[i][j] && field[figY + i][figX + j])
gameOver = true;
}
// ← ПОВОРОТ ФИГУРЫ
void rotateFigure() {
int temp[4][4];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
temp[j][3 - i] = currentFig[i][j];
bool canRotate = true;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (temp[i][j])
if (figX + j < 0 figX + j >= FIELD_W figY + i >= FIELD_H || field[figY + i][figX + j])
canRotate = false;
if (canRotate) {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
currentFig[i][j] = temp[i][j];
// ← ЗВУК ПОВОРОТА (частота, длительность) — можно менять
tone(buzzerPin, 500, 30);
}
}
bool canMove(int dx, int dy) {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (currentFig[i][j]) {
int nx = figX + j + dx;
int ny = figY + i + dy;
if (nx < 0 nx >= FIELD_W ny >= FIELD_H) return false;
if (ny >= 0 && field[ny][nx]) return false;
}
return true;
}
void placeFigure() {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (currentFig[i][j])
field[figY + i][figX + j] = 1;
for (int i = FIELD_H - 1; i >= 0; i--) {
bool full = true;
for (int j = 0; j < FIELD_W; j++)
if (!field[i][j]) full = false;
if (full) {
for (int k = i; k > 0; k--)
for (int j = 0; j < FIELD_W; j++)
field[k][j] = field[k - 1][j];
for (int j = 0; j < FIELD_W; j++) field[0][j] = 0;
score += 100; // ← ОЧКИ ЗА ЛИНИЮ (можно 50, 200)
if (dropSpeed > 200) dropSpeed -= 50; // ← УСКОРЕНИЕ ЗА ЛИНИЮ (50 — плавно, 100 — резко)
// ← ЗВУК СОБРАННОЙ ЛИНИИ (частота, длительность) — можно менять
tone(buzzerPin, 1000, 80);
i++;
}
}
spawnFigure();
}
void setup() {
Wire.begin(5, 6);
pinMode(joySW, INPUT_PULLUP);
pinMode(buzzerPin, OUTPUT); // ← ЗУММЕР
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.display();
randomSeed(analogRead(0));
for (int i = 0; i < FIELD_H; i++)
for (int j = 0; j < FIELD_W; j++)
field[i][j] = 0;
spawnFigure();
// ← ЗВУК СТАРТА
tone(buzzerPin, 700, 80);
}
void loop() {
if (gameOver) {
if (score > highScore) highScore = score;
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(15, 10);
display.println("GAME");
display.setCursor(25, 30);
display.println("OVER");
display.setTextSize(1);
display.setCursor(10, 50);
display.print("S:"); display.print(score);
display.setCursor(60, 50);
display.print("R:"); display.print(highScore);
display.display();
// ← ЗВУК ПРОИГРЫША (три нисходящих тона) — можно менять частоты и длительности
tone(buzzerPin, 500, 80);
delay(100);
tone(buzzerPin, 400, 80);
delay(100);
tone(buzzerPin, 300, 120);
delay(500);
if (digitalRead(joySW) == LOW) {
gameOver = false; score = 0; dropSpeed = 800;
for (int i = 0; i < FIELD_H; i++)
for (int j = 0; j < FIELD_W; j++)
field[i][j] = 0;
spawnFigure();
// ← ЗВУК РЕСТАРТА
tone(buzzerPin, 700, 80);
delay(300);
}
return;
}
static bool lastLeft = false, lastRight = false, lastDown = false, lastSW = false;
int xVal = analogRead(joyX);
int yVal = analogRead(joyY);
bool sw = (digitalRead(joySW) == LOW);
// ← УПРАВЛЕНИЕ ВЛЕВО
if (xVal < 1000 && !lastLeft) {
if (canMove(-1, 0)) figX--;
lastLeft = true;
}
if (xVal >= 1000) lastLeft = false;
// ← УПРАВЛЕНИЕ ВПРАВО
if (xVal > 3000 && !lastRight) {
if (canMove(1, 0)) figX++;
lastRight = true;
}
if (xVal <= 3000) lastRight = false;
// ← УСКОРЕНИЕ ВНИЗ
if (yVal > 3000 && !lastDown) {
if (canMove(0, 1)) figY++;
lastDown = true;
}
if (yVal <= 3000) lastDown = false;
// ← ПОВОРОТ КНОПКОЙ SW
if (sw && !lastSW) {
rotateFigure();
lastSW = true;
}
if (!sw) lastSW = false;
// ← АВТОПАДЕНИЕ
if (millis() - lastDrop > (yVal > 3000 ? 50 : dropSpeed)) {
if (canMove(0, 1)) {
figY++;
} else {
placeFigure();
}
lastDrop = millis();
}
display.clearDisplay();
// ← ПОЛЕ (можно убрать рамку — закомментируй строку)
display.drawRect(FIELD_X - 1, FIELD_Y - 1, FIELD_W * CELL_SIZE + 2, FIELD_H * CELL_SIZE + 2, SSD1306_WHITE);
for (int i = 0; i < FIELD_H; i++)
for (int j = 0; j < FIELD_W; j++)
if (field[i][j])
display.fillRect(FIELD_X + j * CELL_SIZE, FIELD_Y + i * CELL_SIZE, CELL_SIZE, CELL_SIZE, SSD1306_WHITE);
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (currentFig[i][j])
display.fillRect(FIELD_X + (figX + j) * CELL_SIZE, FIELD_Y + (figY + i) * CELL_SIZE, CELL_SIZE, CELL_SIZE, SSD1306_WHITE);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("S:"); display.print(score);
display.setCursor(80, 0);
display.print("R:"); display.print(highScore);
display.display();
delay(30);
}
🧱🔔 ДЕНЬ 20: ТЕТРИС СО ЗВУКОМ НА ESP32
Привет, это Вадим! 👋
Сегодня на YouTube вышел двадцатый урок. Добавляем звуки к Тетрису! Поворот пищит, линия собралась — сигнал, проигрыш — грустный тон! 🧱🔔
---
📋 СОДЕРЖАНИЕ:
1️⃣ Подключение зуммера
2️⃣ Добавление звуков в код
3️⃣ Игровой процесс со звуками
---
🛠 ЧТО НУЖНО:
🔹 Плата ESP32-C3
🔹 Джойстик KY-023
🔹 OLED-дисплей 0.96 дюйма, I2C, SSD1306
🔹 Зуммер (активный, KY-012)
🔹 Провода мама-мама (10 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
OLED-дисплей:
Дисплей ESP32-C3
VCC 3.3V
GND GND
SCL GPIO 6
SDA GPIO 5
Джойстик KY-023:
Джойстик ESP32-C3
+5V 3.3V
GND GND
VRx GPIO 0
VRy GPIO 1
SW GPIO 2
Зуммер:
Зуммер ESP32-C3
S GPIO 7
Средний 3.3V
- GND
---
📄 platformio.ini:
---
💻 КОД (main.cpp):
Код под этим постом 👇
---
⚙️ НАСТРОЙКА ТЕТРИСА:
1. Скорость падения:
2. Ускорение за линию:
3. Очки за линию:
4. Размер поля:
🔊 НАСТРОЙКА ЗВУКА:
5. Звук поворота:
6. Звук собранной линии:
Можно заменить на (buzzerPin, 800, 100) — ниже и длиннее.
7. Звук проигрыша:
8. Отключить звук совсем:
Удали или закомментируй все строки tone(buzzerPin, ...); — игра будет без звука.
---
📺 СМОТРЕТЬ ВИДЕОУРОК:
👉 https://youtube.com/shorts/fU2pwpYCz1Q?si=lA6RX_RLimJPB-wS
---
📅 СЛЕДУЮЩИЙ УРОК:
Через 1–2 дня — Тир на ESP32! 🎯💥
Не пропусти! 🔔
---
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
---
Привет, это Вадим! 👋
Сегодня на YouTube вышел двадцатый урок. Добавляем звуки к Тетрису! Поворот пищит, линия собралась — сигнал, проигрыш — грустный тон! 🧱🔔
---
📋 СОДЕРЖАНИЕ:
1️⃣ Подключение зуммера
2️⃣ Добавление звуков в код
3️⃣ Игровой процесс со звуками
---
🛠 ЧТО НУЖНО:
🔹 Плата ESP32-C3
🔹 Джойстик KY-023
🔹 OLED-дисплей 0.96 дюйма, I2C, SSD1306
🔹 Зуммер (активный, KY-012)
🔹 Провода мама-мама (10 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
OLED-дисплей:
Дисплей ESP32-C3
VCC 3.3V
GND GND
SCL GPIO 6
SDA GPIO 5
Джойстик KY-023:
Джойстик ESP32-C3
+5V 3.3V
GND GND
VRx GPIO 0
VRy GPIO 1
SW GPIO 2
Зуммер:
Зуммер ESP32-C3
S GPIO 7
Средний 3.3V
- GND
---
📄 platformio.ini:
[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
build_flags =
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
adafruit/Adafruit GFX Library@^1.11.11
adafruit/Adafruit SSD1306@^2.5.13
---
💻 КОД (main.cpp):
Код под этим постом 👇
---
⚙️ НАСТРОЙКА ТЕТРИСА:
1. Скорость падения:
int dropSpeed = 800; // 400 — быстро, 1200 — медленно
2. Ускорение за линию:
if (dropSpeed > 200) dropSpeed -= 50; // 50 — плавно, 100 — резко
3. Очки за линию:
score += 100; // можно 50, 200
4. Размер поля:
#define FIELD_W 10 // ширина поля
#define FIELD_H 12 // высота поля (можно 16, но уменьшить CELL_SIZE)
#define CELL_SIZE 5 // размер клетки (4 — мельче, 6 — крупнее)
🔊 НАСТРОЙКА ЗВУКА:
5. Звук поворота:
tone(buzzerPin, 500, 30); // (частота, длительность) — 500 Гц, 30 мс
Выше частота → тоньше писк. Больше длительность → длиннее звук.
6. Звук собранной линии:
tone(buzzerPin, 1000, 80); // 1000 Гц — высокий сигнал
Можно заменить на (buzzerPin, 800, 100) — ниже и длиннее.
7. Звук проигрыша:
tone(buzzerPin, 500, 80); // первый тон
delay(100);
tone(buzzerPin, 400, 80); // второй тон
delay(100);
tone(buzzerPin, 300, 120); // третий тон (ниже и длиннее)
Можно менять частоты (500, 400, 300) и длительности (80, 80, 120).
8. Отключить звук совсем:
Удали или закомментируй все строки tone(buzzerPin, ...); — игра будет без звука.
---
📺 СМОТРЕТЬ ВИДЕОУРОК:
👉 https://youtube.com/shorts/fU2pwpYCz1Q?si=lA6RX_RLimJPB-wS
---
📅 СЛЕДУЮЩИЙ УРОК:
Через 1–2 дня — Тир на ESP32! 🎯💥
Не пропусти! 🔔
---
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
---
Telegram
ESP: от новичка до профи
🎓 Уроки для новичков: просто, понятно, без сложных слов.
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
❌⭕ ДЕНЬ 21: КРЕСТИКИ-НОЛИКИ НА ESP32
Привет, это Вадим! 👋
Сегодня на YouTube вышел двадцать первый урок. Делаем классическую игру — Крестики-нолики на ESP32! Играем против бота, выбираем сложность, счёт сохраняется! ❌⭕🎮
---
📋 СОДЕРЖАНИЕ:
1️⃣ Подключение джойстика и дисплея
2️⃣ Полный код игры с 3 уровнями сложности
3️⃣ Игровой процесс — победи бота!
---
🛠 ЧТО НУЖНО:
🔹 Плата ESP32-C3
🔹 Джойстик KY-023
🔹 OLED-дисплей 0.96 дюйма, I2C, SSD1306
🔹 Провода мама-мама (7 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
OLED-дисплей:
| Дисплей | ESP32-C3 |
|---------|----------|
| VCC | 3.3V |
| GND | GND |
| SCL | GPIO 6 |
| SDA | GPIO 5 |
Джойстик KY-023:
| Джойстик | ESP32-C3 |
|----------|----------|
| +5V | 3.3V |
| GND | GND |
| VRx | GPIO 0 |
| VRy | GPIO 1 |
| SW | GPIO 2 |
---
📄 platformio.ini:
---
💻 КОД (main.cpp):
Код под этим постом 👇
---
⚙️ НАСТРОЙКА ИГРЫ:
В коде есть параметры, которые можно изменить под себя:
1. Сложность бота:
Найди строку:
Меняй цифру 1, 2 или 3. Также можно выбрать сложность джойстиком в меню перед началом игры.
1. Скорость хода бота:
Найди строку:
Чем меньше число (например 200), тем быстрее ходит бот. 1000 — медленно.
1. Логика бота:
· Лёгкий (1) — случайные ходы
· Средний (2) — может выиграть и заблокировать игрока
· Сложный (3) — средний + центр + углы
---
🎮 КАК ИГРАТЬ:
1. На старте выбери сложность джойстиком (влево/вправо)
2. Нажми джойстик (SW) для старта
3. Управляй курсором джойстиком
4. Нажми джойстик, чтобы поставить X
5. Бот ходит автоматически
6. После победы/ничьей — нажми SW для новой игры
7. Счёт сохраняется до перезагрузки
---
📺 СМОТРЕТЬ ВИДЕОУРОК:
👉 https://youtube.com/shorts/ZUdzamNdBNg?si=iR-SC_la6tMvsJST
---
📅 СЛЕДУЮЩИЙ УРОК:
Через 1–2 дня — начинаем большой проект «Умный цветок»! Датчик CO₂, влажность почвы, Telegram-бот. Не пропусти! 🌱🤖
---
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
Привет, это Вадим! 👋
Сегодня на YouTube вышел двадцать первый урок. Делаем классическую игру — Крестики-нолики на ESP32! Играем против бота, выбираем сложность, счёт сохраняется! ❌⭕🎮
---
📋 СОДЕРЖАНИЕ:
1️⃣ Подключение джойстика и дисплея
2️⃣ Полный код игры с 3 уровнями сложности
3️⃣ Игровой процесс — победи бота!
---
🛠 ЧТО НУЖНО:
🔹 Плата ESP32-C3
🔹 Джойстик KY-023
🔹 OLED-дисплей 0.96 дюйма, I2C, SSD1306
🔹 Провода мама-мама (7 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
OLED-дисплей:
| Дисплей | ESP32-C3 |
|---------|----------|
| VCC | 3.3V |
| GND | GND |
| SCL | GPIO 6 |
| SDA | GPIO 5 |
Джойстик KY-023:
| Джойстик | ESP32-C3 |
|----------|----------|
| +5V | 3.3V |
| GND | GND |
| VRx | GPIO 0 |
| VRy | GPIO 1 |
| SW | GPIO 2 |
---
📄 platformio.ini:
[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
build_flags =
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
adafruit/Adafruit GFX Library@^1.11.11
adafruit/Adafruit SSD1306@^2.5.13
---
💻 КОД (main.cpp):
Код под этим постом 👇
---
⚙️ НАСТРОЙКА ИГРЫ:
В коде есть параметры, которые можно изменить под себя:
1. Сложность бота:
Найди строку:
int difficulty = 2; // 1=легко, 2=средне, 3=сложно
Меняй цифру 1, 2 или 3. Также можно выбрать сложность джойстиком в меню перед началом игры.
1. Скорость хода бота:
Найди строку:
if (botWaiting && millis() - botTimer > 500) { // Задержка 500 мсЧем меньше число (например 200), тем быстрее ходит бот. 1000 — медленно.
1. Логика бота:
· Лёгкий (1) — случайные ходы
· Средний (2) — может выиграть и заблокировать игрока
· Сложный (3) — средний + центр + углы
---
🎮 КАК ИГРАТЬ:
1. На старте выбери сложность джойстиком (влево/вправо)
2. Нажми джойстик (SW) для старта
3. Управляй курсором джойстиком
4. Нажми джойстик, чтобы поставить X
5. Бот ходит автоматически
6. После победы/ничьей — нажми SW для новой игры
7. Счёт сохраняется до перезагрузки
---
📺 СМОТРЕТЬ ВИДЕОУРОК:
👉 https://youtube.com/shorts/ZUdzamNdBNg?si=iR-SC_la6tMvsJST
---
📅 СЛЕДУЮЩИЙ УРОК:
Через 1–2 дня — начинаем большой проект «Умный цветок»! Датчик CO₂, влажность почвы, Telegram-бот. Не пропусти! 🌱🤖
---
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
Telegram
ESP: от новичка до профи
🎓 Уроки для новичков: просто, понятно, без сложных слов.
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
2 часть кода крестики нолики
if (checkWin() == 'O') return;
board[y][x] = ' ';
}
}
}
// Блокировка игрока
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
if (board[y][x] == ' ') {
board[y][x] = 'X';
if (checkWin() == 'X') {
board[y][x] = 'O';
return;
}
board[y][x] = ' ';
}
}
}
}
// Лёгкий бот: случайный ход
if (difficulty == 1) {
int emptyCells[9][2];
int count = 0;
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
if (board[y][x] == ' ') {
emptyCells[count][0] = y;
emptyCells[count][1] = x;
count++;
}
if (count > 0) {
int r = random(count);
board[emptyCells[r][0]][emptyCells[r][1]] = 'O';
}
return;
}
// Центр
if (board[1][1] == ' ') { board[1][1] = 'O'; return; }
// Углы
int corners[4][2] = {{0,0}, {0,2}, {2,0}, {2,2}};
for (int i = 0; i < 4; i++) {
if (board[corners[i][0]][corners[i][1]] == ' ') {
board[corners[i][0]][corners[i][1]] = 'O';
return;
}
}
// Любая свободная
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
if (board[y][x] == ' ') { board[y][x] = 'O'; return; }
}
void resetGame() {
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
board[y][x] = ' ';
gameOver = false;
winner = ' ';
cursorX = 1;
cursorY = 1;
playerTurn = true;
}
void setup() {
Wire.begin(5, 6);
pinMode(joySW, INPUT_PULLUP);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.display();
randomSeed(analogRead(0));
}
void loop() {
// Выбор сложности
if (!gameStarted) {
static bool lastLeft2 = false, lastRight2 = false;
int xVal = analogRead(joyX);
if (xVal < 1000 && !lastLeft2) { difficulty--; if (difficulty < 1) difficulty = 1; lastLeft2 = true; }
if (xVal >= 1000) lastLeft2 = false;
if (xVal > 3000 && !lastRight2) { difficulty++; if (difficulty > 3) difficulty = 3; lastRight2 = true; }
if (xVal <= 3000) lastRight2 = false;
drawMenu();
if (digitalRead(joySW) == LOW) {
gameStarted = true;
drawBoard();
delay(300);
}
return;
}
if (gameOver) {
drawBoard();
if (digitalRead(joySW) == LOW) {
resetGame();
delay(300);
}
return;
}
// Управление курсором
static bool lastLeft = false, lastRight = false, lastUp = false, lastDown = false, lastSW = false;
int xVal = analogRead(joyX);
int yVal = analogRead(joyY);
bool sw = (digitalRead(joySW) == LOW);
if (xVal < 1000 && !lastLeft) { cursorX--; if (cursorX < 0) cursorX = 0; lastLeft = true; }
if (xVal >= 1000) lastLeft = false;
if (xVal > 3000 && !lastRight) { cursorX++; if (cursorX > 2) cursorX = 2; lastRight = true; }
if (xVal <= 3000) lastRight = false;
if (yVal < 1000 && !lastUp) { cursorY--; if (cursorY < 0) cursorY = 0; lastUp = true; }
if (yVal >= 1000) lastUp = false;
if (yVal > 3000 && !lastDown) { cursorY++; if (cursorY > 2) cursorY = 2; lastDown = true; }
if (yVal <= 3000) lastDown = false;
// Ход игрока
if (sw && !lastSW && playerTurn && board[cursorY][cursorX] == ' ') {
board[cursorY][cursorX] = 'X';
lastSW = true;
winner = checkWin();
if (winner != ' ') {
gameOver = true;
if (winner == 'X') scorePlayer++;
else if (winner == 'O') scoreBot++;
else scoreDraw++;
} else {
playerTurn = false;
}
}
if (!sw) lastSW = false;
// Ход бота с задержкой
static unsigned long botTimer = 0;
static bool botWaiting = false;
if (!playerTurn && !gameOver && !botWaiting) {
botTimer = millis();
botWaiting = true;
}
if (botWaiting && millis() - botTimer > 500) { // Задержка 500 мс
3 часть кода крестики нолики
botMove();
botWaiting = false;
winner = checkWin();
if (winner != ' ') {
gameOver = true;
if (winner == 'X') scorePlayer++;
else if (winner == 'O') scoreBot++;
else scoreDraw++;
} else {
playerTurn = true;
}
}
drawBoard();
delay(50);
}
[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
build_flags =
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
adafruit/Adafruit GFX Library@^1.11.11
adafruit/Adafruit SSD1306@^2.5.13
1 часть кода крестики нолики
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
const int joyX = 0;
const int joyY = 1;
const int joySW = 2;
char board[3][3] = {
{' ', ' ', ' '},
{' ', ' ', ' '},
{' ', ' ', ' '}
};
int cursorX = 1, cursorY = 1;
bool gameOver = false;
char winner = ' ';
int scorePlayer = 0, scoreBot = 0, scoreDraw = 0;
bool playerTurn = true;
int difficulty = 2; // 1=легко, 2=средне, 3=сложно
bool gameStarted = false;
void drawBoard() {
display.clearDisplay();
// Сетка
display.drawLine(42, 5, 42, 55, SSD1306_WHITE);
display.drawLine(84, 5, 84, 55, SSD1306_WHITE);
display.drawLine(5, 22, 120, 22, SSD1306_WHITE);
display.drawLine(5, 40, 120, 40, SSD1306_WHITE);
// Крестики и нолики
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
int cx = 24 + x * 42;
int cy = 13 + y * 18;
if (board[y][x] == 'X') {
display.drawLine(cx - 8, cy - 8, cx + 8, cy + 8, SSD1306_WHITE);
display.drawLine(cx + 8, cy - 8, cx - 8, cy + 8, SSD1306_WHITE);
} else if (board[y][x] == 'O') {
display.drawCircle(cx, cy, 9, SSD1306_WHITE);
}
}
}
// Курсор
if (!gameOver && playerTurn) {
int cx = 24 + cursorX * 42;
int cy = 13 + cursorY * 18;
display.drawRect(cx - 11, cy - 10, 23, 21, SSD1306_WHITE);
}
// Счёт
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 56);
display.print("X:"); display.print(scorePlayer);
display.setCursor(45, 56);
display.print("O:"); display.print(scoreBot);
display.setCursor(85, 56);
display.print("N:"); display.print(scoreDraw);
// Статус
display.setCursor(0, 0);
if (!gameStarted) {
display.print("Pick level: ");
if (difficulty == 1) display.print("EASY");
else if (difficulty == 2) display.print("MEDIUM");
else display.print("HARD");
display.setCursor(0, 10);
display.print("Press SW");
} else if (gameOver) {
if (winner == 'X') display.print("You win!");
else if (winner == 'O') display.print("Bot wins!");
else display.print("Draw!");
} else {
display.print(playerTurn ? "Your turn" : "Bot thinking...");
}
display.display();
}
void drawMenu() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(15, 5);
display.println("TicTac");
display.setCursor(30, 25);
display.println("Toe");
display.setTextSize(1);
display.setCursor(10, 45);
display.print("Level: ");
if (difficulty == 1) display.print("EASY");
else if (difficulty == 2) display.print("MEDIUM");
else display.print("HARD");
display.setCursor(10, 55);
display.print("Press SW to start");
display.display();
}
char checkWin() {
for (int y = 0; y < 3; y++) {
if (board[y][0] != ' ' && board[y][0] == board[y][1] && board[y][1] == board[y][2])
return board[y][0];
}
for (int x = 0; x < 3; x++) {
if (board[0][x] != ' ' && board[0][x] == board[1][x] && board[1][x] == board[2][x])
return board[0][x];
}
if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[1][1] == board[2][2])
return board[0][0];
if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[1][1] == board[2][0])
return board[0][2];
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
if (board[y][x] == ' ') return ' ';
return 'N';
}
void botMove() {
// Сложный бот: может выиграть или заблокировать
if (difficulty >= 2) {
// Проверка победы бота
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
if (board[y][x] == ' ') {
board[y][x] = 'O';
🎮 ДЕНЬ 22: GEOMETRY DASH НА ESP32
Привет, это Вадим! 👋
Сегодня на YouTube вышел двадцать второй урок. Делаем игру Geometry Dash на ESP32! Квадратик прыгает, уклоняется от препятствий, набирает очки! 🎮🟦
---
📋 СОДЕРЖАНИЕ:
1️⃣ Подключение джойстика и дисплея
2️⃣ Полный код игры
3️⃣ Игровой процесс — ставь рекорды!
---
🛠 ЧТО НУЖНО:
🔹 Плата ESP32-C3
🔹 Джойстик KY-023
🔹 OLED-дисплей 0.96 дюйма, I2C, SSD1306
🔹 Провода мама-мама (7 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
OLED-дисплей:
| Дисплей | ESP32-C3 |
|---------|----------|
| VCC | 3.3V |
| GND | GND |
| SCL | GPIO 6 |
| SDA | GPIO 5 |
Джойстик KY-023:
| Джойстик | ESP32-C3 |
|----------|----------|
| VCC | 3.3V |
| GND | GND |
| VRx | GPIO 0 |
| VRy | GPIO 1 |
| SW | GPIO 2 |
---
📄 platformio.ini:
---
💻 КОД (main.cpp):
Код под этим постом 👇
---
⚙️ НАСТРОЙКА ИГРЫ:
В коде есть параметры, которые можно изменить под себя:
1. Скорость игры:
Найди строку:
Чем больше число, тем быстрее бег. 6 — быстро, 3 — медленно.
2. Сила прыжка:
Найди строку:
Чем больше число (по модулю), тем выше прыжок. -9 — высоко, -5 — низко.
3. Ускорение за очки:
Найди строку:
Меняй 5 на 3 — ускорение каждые 3 очка. Меняй 10 на 15 — макс. скорость.
4. Гравитация:
Найди строку:
2 — сильное притяжение (быстро падает), 1 — норма.
---
🎮 КАК ИГРАТЬ:
1. Загрузи код на ESP32
2. Джойстиком вверх или нажатием SW — прыжок
3. Уклоняйся от препятствий
4. Каждые 5 очков — скорость растёт
5. При столкновении — Game Over
6. Нажми SW для перезапуска
---
📺 СМОТРЕТЬ ВИДЕОУРОК:
👉 https://youtube.com/shorts/F29OR57jPpA?si=7KGeAMaLWoeNG69P
---
📅 СЛЕДУЮЩИЙ УРОК:
Через 1–2 дня — Кубик-раннер на ESP32! Ещё один раннер, но с другой механикой. Не пропусти! 🏃♂️
---
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
Привет, это Вадим! 👋
Сегодня на YouTube вышел двадцать второй урок. Делаем игру Geometry Dash на ESP32! Квадратик прыгает, уклоняется от препятствий, набирает очки! 🎮🟦
---
📋 СОДЕРЖАНИЕ:
1️⃣ Подключение джойстика и дисплея
2️⃣ Полный код игры
3️⃣ Игровой процесс — ставь рекорды!
---
🛠 ЧТО НУЖНО:
🔹 Плата ESP32-C3
🔹 Джойстик KY-023
🔹 OLED-дисплей 0.96 дюйма, I2C, SSD1306
🔹 Провода мама-мама (7 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
OLED-дисплей:
| Дисплей | ESP32-C3 |
|---------|----------|
| VCC | 3.3V |
| GND | GND |
| SCL | GPIO 6 |
| SDA | GPIO 5 |
Джойстик KY-023:
| Джойстик | ESP32-C3 |
|----------|----------|
| VCC | 3.3V |
| GND | GND |
| VRx | GPIO 0 |
| VRy | GPIO 1 |
| SW | GPIO 2 |
---
📄 platformio.ini:
[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
build_flags =
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
adafruit/Adafruit GFX Library@^1.11.11
adafruit/Adafruit SSD1306@^2.5.13
---
💻 КОД (main.cpp):
Код под этим постом 👇
---
⚙️ НАСТРОЙКА ИГРЫ:
В коде есть параметры, которые можно изменить под себя:
1. Скорость игры:
Найди строку:
int gameSpeed = 4;
Чем больше число, тем быстрее бег. 6 — быстро, 3 — медленно.
2. Сила прыжка:
Найди строку:
jumpVelocity = -7;
Чем больше число (по модулю), тем выше прыжок. -9 — высоко, -5 — низко.
3. Ускорение за очки:
Найди строку:
if (score % 5 == 0 && gameSpeed < 10)
Меняй 5 на 3 — ускорение каждые 3 очка. Меняй 10 на 15 — макс. скорость.
4. Гравитация:
Найди строку:
int gravity = 1;
2 — сильное притяжение (быстро падает), 1 — норма.
---
🎮 КАК ИГРАТЬ:
1. Загрузи код на ESP32
2. Джойстиком вверх или нажатием SW — прыжок
3. Уклоняйся от препятствий
4. Каждые 5 очков — скорость растёт
5. При столкновении — Game Over
6. Нажми SW для перезапуска
---
📺 СМОТРЕТЬ ВИДЕОУРОК:
👉 https://youtube.com/shorts/F29OR57jPpA?si=7KGeAMaLWoeNG69P
---
📅 СЛЕДУЮЩИЙ УРОК:
Через 1–2 дня — Кубик-раннер на ESP32! Ещё один раннер, но с другой механикой. Не пропусти! 🏃♂️
---
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
Telegram
ESP: от новичка до профи
🎓 Уроки для новичков: просто, понятно, без сложных слов.
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Пины джойстика
const int joySW = 2;
const int joyX = 0;
const int joyY = 1;
// Игровые переменные
int playerX = 30;
int playerY = 50;
int playerSize = 8;
bool isJumping = false;
int jumpVelocity = 0;
int gravity = 1;
int obstacleX = 128;
int obstacleWidth = 8;
int obstacleHeight = 10;
int obstacleY = 50;
int score = 0;
int highScore = 0;
int gameSpeed = 4;
bool gameOver = false;
void jump() {
if (!isJumping && !gameOver) {
isJumping = true;
jumpVelocity = -7;
}
}
void resetGame() {
gameOver = false;
score = 0;
gameSpeed = 4;
playerY = 50;
isJumping = false;
jumpVelocity = 0;
obstacleX = 128;
}
void drawGame() {
display.clearDisplay();
// Земля
display.fillRect(0, 58, SCREEN_WIDTH, 6, SSD1306_WHITE);
// Игрок (квадратик)
display.fillRect(playerX, playerY, playerSize, playerSize, SSD1306_WHITE);
// Препятствие
display.fillRect(obstacleX, obstacleY, obstacleWidth, obstacleHeight, SSD1306_WHITE);
// Счёт
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Score: ");
display.print(score);
display.setCursor(0, 8);
display.print("Best: ");
display.print(highScore);
if (gameOver) {
display.setTextSize(1);
display.setCursor(25, 28);
display.print("GAME OVER");
display.setCursor(20, 38);
display.print("Press SW");
}
display.display();
}
void setup() {
Wire.begin(5, 6);
pinMode(joySW, INPUT_PULLUP);
pinMode(joyX, INPUT);
pinMode(joyY, INPUT);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.display();
}
void loop() {
// Прыжок по нажатию SW
static bool lastSW = false;
bool sw = (digitalRead(joySW) == LOW);
if (sw && !lastSW) {
jump();
}
lastSW = sw;
// Можно прыгать и джойстиком вверх (для удобства)
int yVal = analogRead(joyY);
static bool lastUp = false;
bool up = (yVal < 1000);
if (up && !lastUp) {
jump();
}
lastUp = up;
if (!gameOver) {
// Физика прыжка
if (isJumping) {
playerY += jumpVelocity;
jumpVelocity += gravity;
if (playerY >= 50) {
playerY = 50;
isJumping = false;
jumpVelocity = 0;
}
}
// Движение препятствия
obstacleX -= gameSpeed;
// Новое препятствие
if (obstacleX < -obstacleWidth) {
obstacleX = SCREEN_WIDTH;
score++;
if (score > highScore) highScore = score;
// Увеличение скорости каждые 5 очков
if (score % 5 == 0 && gameSpeed < 10) {
gameSpeed++;
}
}
// Столкновение
if (playerX + playerSize > obstacleX &&
playerX < obstacleX + obstacleWidth &&
playerY + playerSize > obstacleY) {
gameOver = true;
}
} else {
// Перезапуск по нажатию SW
static bool resetSW = false;
if (sw && !resetSW) {
resetGame();
}
resetSW = sw;
}
drawGame();
delay(20);
}
🌿 ДЕНЬ 23: ДАТЧИК CO₂ SCD41 + OLED
Привет, это Вадим! 👋
Сегодня подключаем датчик углекислого газа SCD41 к ESP32-C3 и выводим данные на OLED.
---
📋 СОДЕРЖАНИЕ УРОКА:
1️⃣ Подключение SCD41 и OLED по I2C
2️⃣ Полный код для чтения данных
3️⃣ Демонстрация: CO₂, температура, влажность
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
SCD41 → ESP32-C3:
- VDD → 3.3V
- GND → GND
- SCL → GPIO 6
- SDA → GPIO 5
OLED → ESP32-C3:
- VCC → 3.3V
- GND → GND
- SCL → GPIO 6
- SDA → GPIO 5
Оба устройства на одной шине I2C!
---
💻 КОД:
Код в следующем сообщении👇
---
📺 СМОТРЕТЬ ВИДЕО:
👉 https://youtube.com/shorts/gUeUDuXk2_8?si=I9j05mDm0WpRPc6U
---
📅 ЗАВТРА: подключаем датчик влажности почвы и продолжаем Умный цветок!
💬 Вопросы: https://t.me/espSVADIM?direct
Привет, это Вадим! 👋
Сегодня подключаем датчик углекислого газа SCD41 к ESP32-C3 и выводим данные на OLED.
---
📋 СОДЕРЖАНИЕ УРОКА:
1️⃣ Подключение SCD41 и OLED по I2C
2️⃣ Полный код для чтения данных
3️⃣ Демонстрация: CO₂, температура, влажность
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
SCD41 → ESP32-C3:
- VDD → 3.3V
- GND → GND
- SCL → GPIO 6
- SDA → GPIO 5
OLED → ESP32-C3:
- VCC → 3.3V
- GND → GND
- SCL → GPIO 6
- SDA → GPIO 5
Оба устройства на одной шине I2C!
---
💻 КОД:
Код в следующем сообщении👇
---
📺 СМОТРЕТЬ ВИДЕО:
👉 https://youtube.com/shorts/gUeUDuXk2_8?si=I9j05mDm0WpRPc6U
---
📅 ЗАВТРА: подключаем датчик влажности почвы и продолжаем Умный цветок!
💬 Вопросы: https://t.me/espSVADIM?direct
Telegram
ESP: от новичка до профи
🎓 Уроки для новичков: просто, понятно, без сложных слов.
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
А вот и код для дня 23:
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#define SCD41_ADDR 0x62
#define SCD41_CMD_START_PERIODIC 0x21B1
#define SCD41_CMD_READ_DATA 0xEC05
#define SCD41_CMD_GET_DATA_READY 0xE4B8
uint16_t co2 = 0;
float temperature = 0.0;
float humidity = 0.0;
void writeCommand(uint16_t cmd) {
Wire.beginTransmission(SCD41_ADDR);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
Wire.endTransmission();
}
void writeCommandWithStop(uint16_t cmd) {
Wire.beginTransmission(SCD41_ADDR);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
Wire.endTransmission(true);
}
bool isDataReady() {
Wire.beginTransmission(SCD41_ADDR);
Wire.write(SCD41_CMD_GET_DATA_READY >> 8);
Wire.write(SCD41_CMD_GET_DATA_READY & 0xFF);
Wire.endTransmission(false);
Wire.requestFrom(SCD41_ADDR, 3);
if (Wire.available() >= 3) {
uint16_t ready = (Wire.read() << 8) | Wire.read();
return (ready & 0x07FF) != 0;
}
return false;
}
void readData() {
writeCommandWithStop(SCD41_CMD_READ_DATA);
delay(3);
Wire.requestFrom(SCD41_ADDR, 9);
if (Wire.available() >= 9) {
uint8_t buffer[9];
for (int i = 0; i < 9; i++) {
buffer[i] = Wire.read();
}
co2 = (buffer[0] << 8) | buffer[1];
uint16_t tempRaw = (buffer[3] << 8) | buffer[4];
temperature = -45.0 + 175.0 * tempRaw / 65535.0;
uint16_t humRaw = (buffer[6] << 8) | buffer[7];
humidity = 100.0 * humRaw / 65535.0;
}
}
void setup() {
Serial.begin(115200);
delay(2000);
Wire.begin(5, 6);
Wire.setClock(100000);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(20, 25);
display.println("SCD41 START");
display.display();
writeCommandWithStop(SCD41_CMD_START_PERIODIC);
delay(1000);
display.clearDisplay();
display.setCursor(10, 25);
display.println("SCD41 READY");
display.display();
delay(1000);
}
void loop() {
if (isDataReady()) {
readData();
Serial.print("CO2: ");
Serial.print(co2);
Serial.print(" ppm | T: ");
Serial.print(temperature, 1);
Serial.print(" C | H: ");
Serial.print(humidity, 1);
Serial.println(" %");
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("CO2 Monitor");
display.setTextSize(2);
display.setCursor(0, 20);
display.print(co2);
display.println(" ppm");
display.setTextSize(1);
display.setCursor(0, 45);
display.print("T: ");
display.print(temperature, 1);
display.print(" C H: ");
display.print(humidity, 1);
display.println(" %");
display.display();
}
delay(100);
}
💧 ДЕНЬ 24: ДАТЧИК ВЛАЖНОСТИ ПОЧВЫ
Привет, это Вадим! 👋
Сегодня подключаем датчик влажности почвы к ESP32. Теперь знаем, когда пора поливать цветы! 🌱
---
📋 СОДЕРЖАНИЕ УРОКА:
1️⃣ Подключение датчика (3 провода)
2️⃣ Вывод данных на OLED в процентах
3️⃣ Демонстрация: сухо → мокро
---
🛠 КОМПОНЕНТЫ:
🔹 ESP32-C3
🔹 Датчик влажности почвы (щуп)
🔹 OLED 0.96" I2C
🔹 Провода мама-мама (5 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
Датчик почвы → ESP32-C3:
- Красный (VCC) → 3.3V
- Чёрный (GND) → GND
- Жёлтый (SIG) → GPIO 4
OLED → ESP32-C3:
- VCC → 3.3V
- GND → GND
- SDA → GPIO 5
- SCL → GPIO 6
---
📄 platformio.ini:
---
💻 КОД (main.cpp):
---
💡 ПРИНЦИП РАБОТЫ:
Датчик измеряет сопротивление почвы:
· 0% — сухая земля (полив нужен)
· 100% — мокрая земля (всё ок)
---
📺 СМОТРЕТЬ ВИДЕО:
👉 https://youtube.com/shorts/c3YQDdaYt-o?si=HSnsDnCXy7ORstdD
---
📅 ЗАВТРА: подключаем Telegram к датчику CO₂! Бот будет присылать данные на телефон.
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
Привет, это Вадим! 👋
Сегодня подключаем датчик влажности почвы к ESP32. Теперь знаем, когда пора поливать цветы! 🌱
---
📋 СОДЕРЖАНИЕ УРОКА:
1️⃣ Подключение датчика (3 провода)
2️⃣ Вывод данных на OLED в процентах
3️⃣ Демонстрация: сухо → мокро
---
🛠 КОМПОНЕНТЫ:
🔹 ESP32-C3
🔹 Датчик влажности почвы (щуп)
🔹 OLED 0.96" I2C
🔹 Провода мама-мама (5 шт)
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ:
Датчик почвы → ESP32-C3:
- Красный (VCC) → 3.3V
- Чёрный (GND) → GND
- Жёлтый (SIG) → GPIO 4
OLED → ESP32-C3:
- VCC → 3.3V
- GND → GND
- SDA → GPIO 5
- SCL → GPIO 6
---
📄 platformio.ini:
[env:esp32-c3-devkitm-1]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
upload_speed = 921600
build_flags =
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
adafruit/Adafruit SSD1306@^2.5.13
adafruit/Adafruit GFX Library@^1.11.11
adafruit/Adafruit BusIO@^1.14.1
---
💻 КОД (main.cpp):
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#define SOIL_SENSOR_PIN 4
int soilPercent = 0;
void setup() {
Serial.begin(115200);
delay(1000);
Wire.begin(5, 6);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(20, 25);
display.println("Soil Tester");
display.display();
delay(1500);
pinMode(SOIL_SENSOR_PIN, INPUT);
}
void loop() {
int soilMoisture = analogRead(SOIL_SENSOR_PIN);
soilPercent = map(soilMoisture, 4095, 0, 0, 100);
if (soilPercent < 0) soilPercent = 0;
if (soilPercent > 100) soilPercent = 100;
Serial.print("Soil: ");
Serial.print(soilPercent);
Serial.println(" %");
display.clearDisplay();
display.setTextSize(2);
display.setCursor(20, 20);
display.print(soilPercent);
display.print(" %");
display.setTextSize(1);
display.setCursor(15, 50);
display.println("Soil Moisture");
display.display();
delay(1000);
}
---
💡 ПРИНЦИП РАБОТЫ:
Датчик измеряет сопротивление почвы:
· 0% — сухая земля (полив нужен)
· 100% — мокрая земля (всё ок)
---
📺 СМОТРЕТЬ ВИДЕО:
👉 https://youtube.com/shorts/c3YQDdaYt-o?si=HSnsDnCXy7ORstdD
---
📅 ЗАВТРА: подключаем Telegram к датчику CO₂! Бот будет присылать данные на телефон.
💬 Есть вопросы? Пиши: https://t.me/espSVADIM?direct
Telegram
ESP: от новичка до профи
🎓 Уроки для новичков: просто, понятно, без сложных слов.
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
🤖 ДЕНЬ 25: ДАТЧИК CO₂ + TELEGRAM на ESP8266
Привет, это Вадим! 👋
Сегодня подключаем датчик CO₂ к Telegram. Теперь данные о воздухе в комнате приходят прямо в телефон! 📱💨
В этом видео: создание бота, подключение датчика, отправка данных.
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ (SCD41 + OLED → ESP8266):
| Компонент | Пин | ESP8266 |
|-----------|-----|---------|
| SCD41 | VDD | 3.3V |
| SCD41 | GND | GND |
| SCD41 | SCL | D1(GPIO5)|
| SCD41 | SDA | D2(GPIO4)|
| OLED | VCC | 3.3V |
| OLED | GND | GND |
| OLED | SCL | D1(GPIO5)|
| OLED | SDA | D2(GPIO4)|
Оба устройства на одной шине I2C (общие SCL и SDA).
---
📄 platformio.ini:
---
Main.cpp
Привет, это Вадим! 👋
Сегодня подключаем датчик CO₂ к Telegram. Теперь данные о воздухе в комнате приходят прямо в телефон! 📱💨
В этом видео: создание бота, подключение датчика, отправка данных.
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ (SCD41 + OLED → ESP8266):
| Компонент | Пин | ESP8266 |
|-----------|-----|---------|
| SCD41 | VDD | 3.3V |
| SCD41 | GND | GND |
| SCD41 | SCL | D1(GPIO5)|
| SCD41 | SDA | D2(GPIO4)|
| OLED | VCC | 3.3V |
| OLED | GND | GND |
| OLED | SCL | D1(GPIO5)|
| OLED | SDA | D2(GPIO4)|
Оба устройства на одной шине I2C (общие SCL и SDA).
---
📄 platformio.ini:
[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_speed = 115200
lib_deps =
witnessmenow/UniversalTelegramBot@^1.3.0
adafruit/Adafruit SSD1306@^2.5.13
adafruit/Adafruit GFX Library@^1.11.11
---
Main.cpp
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <UniversalTelegramBot.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// ========== 📌 ВСТАВЬ СВОИ ДАННЫЕ СЮДА ==========
const char* ssid = "ТВОЙ_WiFi"; // имя твоей сети
const char* password = "ТВОЙ_ПАРОЛЬ"; // пароль от Wi-Fi
const char* botToken = "ТВОЙ_ТОКЕН_БОТА"; // токен от @BotFather
const char* chatId = "ТВОЙ_CHAT_ID"; // твой ID от @userinfobot
// =================================================
WiFiClientSecure client;
UniversalTelegramBot bot(botToken, client);
#define SCD41_ADDR 0x62
#define SCD41_CMD_START_PERIODIC 0x21B1
#define SCD41_CMD_READ_DATA 0xEC05
#define SCD41_CMD_GET_DATA_READY 0xE4B8
uint16_t co2 = 0;
float temperature = 0.0;
float humidity = 0.0;
unsigned long lastSend = 0;
void writeCommand(uint16_t cmd) {
Wire.beginTransmission(SCD41_ADDR);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
Wire.endTransmission();
}
void writeCommandWithStop(uint16_t cmd) {
Wire.beginTransmission(SCD41_ADDR);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
Wire.endTransmission(true);
}
bool isDataReady() {
writeCommand(SCD41_CMD_GET_DATA_READY);
Wire.requestFrom(SCD41_ADDR, 3);
if (Wire.available() >= 3) {
uint16_t ready = (Wire.read() << 8) | Wire.read();
Wire.read();
return (ready & 0x07FF) != 0;
}
return false;
}
void readData() {
writeCommandWithStop(SCD41_CMD_READ_DATA);
delay(3);
Wire.requestFrom(SCD41_ADDR, 9);
if (Wire.available() >= 9) {
uint8_t buffer[9];
for (int i = 0; i < 9; i++) buffer[i] = Wire.read();
co2 = (buffer[0] << 8) | buffer[1];
uint16_t tempRaw = (buffer[3] << 8) | buffer[4];
temperature = -45.0 + 175.0 * tempRaw / 65535.0;
uint16_t humRaw = (buffer[6] << 8) | buffer[7];
humidity = 100.0 * humRaw / 65535.0;
}
}
void setup() {
Serial.begin(115200);
delay(1000);
Wire.begin(4, 5); // SDA на D2 (GPIO4), SCL на D1 (GPIO5)
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(10, 25);
display.println("CO2 + Telegram");
display.display();
writeCommandWithStop(SCD41_CMD_START_PERIODIC);
delay(1000);
Serial.print("Подключение к Wi-Fi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ Wi-Fi подключён!");
client.setInsecure();
display.clearDisplay();
display.setCursor(10, 25);
display.println("WiFi OK");
display.display();
delay(1000);
bot.sendMessage(chatId, "🌿 Умный цветок запущен! Датчик CO₂ работает.", "");
}
void loop() {
if (isDataReady()) {
Telegram
ESP: от новичка до профи
🎓 Уроки для новичков: просто, понятно, без сложных слов.
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
2 часть:
---
⚙️ НАСТРОЙКА ВРЕМЕНИ ОТПРАВКИ:
В коде найди строку:
Число 15000 = 15 секунд. Меняй на нужное:
Значение Время
5000 5 секунд
15000 15 секунд (как в уроке)
30000 30 секунд
60000 1 минута
300000 5 минут
---
📌 КАК ПОЛУЧИТЬ ТОКЕН И CHAT ID:
1. @BotFather → /newbot → получить токен
2. @userinfobot → Start → получить Chat ID
---
📺 СМОТРЕТЬ ВИДЕО:
👉 Полное видео: https://youtu.be/XPk7Rb-qvRg
---
📅 ЗАВТРА: датчик влажности почвы + Telegram!
💬 Вопросы: https://t.me/espSVADIM?direct
readData();
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("CO2 Monitor");
display.setTextSize(2);
display.setCursor(0, 20);
display.print(co2);
display.println(" ppm");
display.setTextSize(1);
display.setCursor(0, 45);
display.print("T: ");
display.print(temperature, 1);
display.print(" C H: ");
display.print(humidity, 1);
display.println(" %");
display.display();
Serial.print("CO2: ");
Serial.print(co2);
Serial.print(" ppm | T: ");
Serial.print(temperature, 1);
Serial.print(" C | H: ");
Serial.print(humidity, 1);
Serial.println(" %");
// ⚙️ НАСТРОЙКА ВРЕМЕНИ ОТПРАВКИ (меняй 15000)
if (millis() - lastSend > 15000) {
String message = "🌿 *Умный цветок*\n";
message += "CO₂: " + String(co2) + " ppm\n";
message += "Температура: " + String(temperature, 1) + " °C\n";
message += "Влажность: " + String(humidity, 1) + " %";
bot.sendMessage(chatId, message, "Markdown");
lastSend = millis();
}
}
delay(500);
}
---
⚙️ НАСТРОЙКА ВРЕМЕНИ ОТПРАВКИ:
В коде найди строку:
if (millis() - lastSend > 15000) {Число 15000 = 15 секунд. Меняй на нужное:
Значение Время
5000 5 секунд
15000 15 секунд (как в уроке)
30000 30 секунд
60000 1 минута
300000 5 минут
---
📌 КАК ПОЛУЧИТЬ ТОКЕН И CHAT ID:
1. @BotFather → /newbot → получить токен
2. @userinfobot → Start → получить Chat ID
---
📺 СМОТРЕТЬ ВИДЕО:
👉 Полное видео: https://youtu.be/XPk7Rb-qvRg
---
📅 ЗАВТРА: датчик влажности почвы + Telegram!
💬 Вопросы: https://t.me/espSVADIM?direct
Telegram
ESP: от новичка до профи
🎓 Уроки для новичков: просто, понятно, без сложных слов.
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
🌿 ДЕНЬ 27: ОБЪЕДИНЯЕМ ДАТЧИКИ CO₂ И ПОЧВЫ
Привет, это Вадим! 👋
Сегодня объединяем оба датчика в одном проекте! Датчик CO₂ и датчик влажности почвы работают вместе. Все данные — на OLED и в Telegram. 📱🌱
---
📋 СОДЕРЖАНИЕ УРОКА:
1️⃣ Подключение двух датчиков + OLED к ESP8266
2️⃣ Код, который читает все показатели
3️⃣ Результат: OLED и Telegram с полными данными
---
🛠 КОМПОНЕНТЫ:
🔹 ESP8266 (NodeMCU)
🔹 Датчик CO₂ SCD41
🔹 Датчик влажности почвы (щуп)
🔹 OLED 0.96" I2C
🔹 Провода мама-мама
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ (все вместе):
Датчик почвы → ESP8266:
• Красный (VCC) → 3.3V
• Чёрный (GND) → GND
• Жёлтый (SIG) → A0
SCD41 (CO₂) → ESP8266:
• VDD → 3.3V
• GND → GND
• SCL → D1 (GPIO5)
• SDA → D2 (GPIO4)
OLED → ESP8266 (общая шина I2C):
• VCC → 3.3V
• GND → GND
• SCL → D1 (GPIO5)
• SDA → D2 (GPIO4)
📌 ВАЖНО: SCD41 и OLED висят на одной шине I2C (общие SCL и SDA). Датчик почвы — на отдельном аналоговом пине A0.
---
📄 platformio.ini:
---
💻 ПОЛНЫЙ КОД (main.cpp):
Код под этим постом 👇
---
⚙️ ЧТО МЕНЯТЬ В КОДЕ:
В коде найди 4 строки и вставь свои данные:
---
⚙️ НАСТРОЙКА ВРЕМЕНИ ОТПРАВКИ:
В коде найди строку:
Число 15000 = 15 секунд. Меняй на нужное:
Значение Время
5000 5 секунд
15000 15 секунд (как в уроке)
30000 30 секунд
60000 1 минута
300000 5 минут
---
📊 ЧТО ПОКАЗЫВАЕТ ПРОЕКТ:
На OLED:
В Telegram приходит полное сообщение со всеми показателями + подсказка о поливе.
💬 Вопросы: https://t.me/espSVADIM?direct
Привет, это Вадим! 👋
Сегодня объединяем оба датчика в одном проекте! Датчик CO₂ и датчик влажности почвы работают вместе. Все данные — на OLED и в Telegram. 📱🌱
---
📋 СОДЕРЖАНИЕ УРОКА:
1️⃣ Подключение двух датчиков + OLED к ESP8266
2️⃣ Код, который читает все показатели
3️⃣ Результат: OLED и Telegram с полными данными
---
🛠 КОМПОНЕНТЫ:
🔹 ESP8266 (NodeMCU)
🔹 Датчик CO₂ SCD41
🔹 Датчик влажности почвы (щуп)
🔹 OLED 0.96" I2C
🔹 Провода мама-мама
---
🔌 СХЕМА ПОДКЛЮЧЕНИЯ (все вместе):
Датчик почвы → ESP8266:
• Красный (VCC) → 3.3V
• Чёрный (GND) → GND
• Жёлтый (SIG) → A0
SCD41 (CO₂) → ESP8266:
• VDD → 3.3V
• GND → GND
• SCL → D1 (GPIO5)
• SDA → D2 (GPIO4)
OLED → ESP8266 (общая шина I2C):
• VCC → 3.3V
• GND → GND
• SCL → D1 (GPIO5)
• SDA → D2 (GPIO4)
📌 ВАЖНО: SCD41 и OLED висят на одной шине I2C (общие SCL и SDA). Датчик почвы — на отдельном аналоговом пине A0.
---
📄 platformio.ini:
[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_speed = 115200
lib_deps =
witnessmenow/UniversalTelegramBot@^1.3.0
adafruit/Adafruit SSD1306@^2.5.13
adafruit/Adafruit GFX Library@^1.11.11
---
💻 ПОЛНЫЙ КОД (main.cpp):
Код под этим постом 👇
---
⚙️ ЧТО МЕНЯТЬ В КОДЕ:
В коде найди 4 строки и вставь свои данные:
const char* ssid = "ТВОЙ_WiFi"; // имя твоей сети
const char* password = "ТВОЙ_ПАРОЛЬ"; // пароль от Wi-Fi
const char* botToken = "ТВОЙ_ТОКЕН_БОТА"; // токен от @BotFather
const char* chatId = "ТВОЙ_CHAT_ID"; // твой ID от @userinfobot
---
⚙️ НАСТРОЙКА ВРЕМЕНИ ОТПРАВКИ:
В коде найди строку:
if (millis() - lastSend > 15000) {Число 15000 = 15 секунд. Меняй на нужное:
Значение Время
5000 5 секунд
15000 15 секунд (как в уроке)
30000 30 секунд
60000 1 минута
300000 5 минут
---
📊 ЧТО ПОКАЗЫВАЕТ ПРОЕКТ:
На OLED:
Smart Flower
CO2: 450 ppm
Temp: 24.5 C
Hum: 55.2 %
Soil: 35 %
В Telegram приходит полное сообщение со всеми показателями + подсказка о поливе.
💬 Вопросы: https://t.me/espSVADIM?direct
Telegram
ESP: от новичка до профи
🎓 Уроки для новичков: просто, понятно, без сложных слов.
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
📹 Видео выходят каждые 1-2 дня на YouTube → смотри и повторяй.
🔗 Все ссылки на товары — в качестве справки, а не рекламы.
💬 Есть вопросы? Напиши мне: t.me/espSVADIM?direct
2 часть:
// ========== SETUP ==========
void setup() {
Serial.begin(115200);
delay(1000);
// I2C для SCD41 и OLED (SDA=D2, SCL=D1)
Wire.begin(4, 5);
// Инициализация OLED
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(10, 25);
display.println("Starting...");
display.display();
// Запуск SCD41
writeCommandWithStop(SCD41_CMD_START_PERIODIC);
delay(1000);
// Датчик почвы
pinMode(SOIL_SENSOR_PIN, INPUT);
// Подключение к Wi-Fi
Serial.print("Подключение к Wi-Fi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ Wi-Fi подключён!");
client.setInsecure();
display.clearDisplay();
display.setCursor(10, 25);
display.println("WiFi OK");
display.display();
delay(1000);
bot.sendMessage(chatId, "🌿 Умный цветок запущен! Оба датчика работают.", "");
}
// ========== LOOP ==========
void loop() {
// Читаем датчик почвы (быстро)
readSoil();
// Читаем CO₂, когда данные готовы
if (isDataReady()) {
readSCD41();
updateDisplay();
Serial.print("CO2: ");
Serial.print(co2);
Serial.print(" ppm | Soil: ");
Serial.print(soilPercent);
Serial.println(" %");
// Отправка в Telegram каждые 15 секунд
if (millis() - lastSend > 15000) {
sendTelegramMessage();
lastSend = millis();
}
}
delay(500);
}
1 часть кода
// ============================================================
// ESP8266 + SCD41 + Датчик почвы + OLED + Telegram
// День 27: Объединение датчиков
// ============================================================
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <UniversalTelegramBot.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// ========== 📌 ВСТАВЬ СВОИ ДАННЫЕ СЮДА ==========
const char* ssid = "ТВОЙ_WiFi"; // имя твоей сети
const char* password = "ТВОЙ_ПАРОЛЬ"; // пароль от Wi-Fi
const char* botToken = "ТВОЙ_ТОКЕН_БОТА"; // токен от @BotFather
const char* chatId = "ТВОЙ_CHAT_ID"; // твой ID от @userinfobot
// =================================================
WiFiClientSecure client;
UniversalTelegramBot bot(botToken, client);
// Пины
#define SOIL_SENSOR_PIN A0 // датчик почвы на A0
#define SCD41_ADDR 0x62
#define SCD41_CMD_START_PERIODIC 0x21B1
#define SCD41_CMD_READ_DATA 0xEC05
#define SCD41_CMD_GET_DATA_READY 0xE4B8
// Переменные для SCD41
uint16_t co2 = 0;
float temperature = 0.0;
float humidity = 0.0;
// Переменные для датчика почвы
int soilPercent = 0;
unsigned long lastSend = 0;
// ========== ФУНКЦИИ ДЛЯ SCD41 ==========
void writeCommand(uint16_t cmd) {
Wire.beginTransmission(SCD41_ADDR);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
Wire.endTransmission();
}
void writeCommandWithStop(uint16_t cmd) {
Wire.beginTransmission(SCD41_ADDR);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
Wire.endTransmission(true);
}
bool isDataReady() {
writeCommand(SCD41_CMD_GET_DATA_READY);
Wire.requestFrom(SCD41_ADDR, 3);
if (Wire.available() >= 3) {
uint16_t ready = (Wire.read() << 8) | Wire.read();
Wire.read(); // CRC
return (ready & 0x07FF) != 0;
}
return false;
}
void readSCD41() {
writeCommandWithStop(SCD41_CMD_READ_DATA);
delay(3);
Wire.requestFrom(SCD41_ADDR, 9);
if (Wire.available() >= 9) {
uint8_t buffer[9];
for (int i = 0; i < 9; i++) buffer[i] = Wire.read();
co2 = (buffer[0] << 8) | buffer[1];
uint16_t tempRaw = (buffer[3] << 8) | buffer[4];
temperature = -45.0 + 175.0 * tempRaw / 65535.0;
uint16_t humRaw = (buffer[6] << 8) | buffer[7];
humidity = 100.0 * humRaw / 65535.0;
}
}
// ========== ФУНКЦИЯ ДЛЯ ДАТЧИКА ПОЧВЫ ==========
void readSoil() {
int soilRaw = analogRead(SOIL_SENSOR_PIN);
soilPercent = map(soilRaw, 1023, 0, 0, 100);
if (soilPercent < 0) soilPercent = 0;
if (soilPercent > 100) soilPercent = 100;
}
// ========== ВЫВОД НА OLED ==========
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Smart Flower");
display.setCursor(0, 12);
display.print("CO2: ");
display.print(co2);
display.println(" ppm");
display.setCursor(0, 24);
display.print("Temp: ");
display.print(temperature, 1);
display.println(" C");
display.setCursor(0, 36);
display.print("Hum: ");
display.print(humidity, 1);
display.println(" %");
display.setCursor(0, 48);
display.print("Soil: ");
display.print(soilPercent);
display.println(" %");
display.display();
}
// ========== ОТПРАВКА В TELEGRAM ==========
void sendTelegramMessage() {
String message = "🌿 *Умный цветок*\n";
message += "CO₂: " + String(co2) + " ppm\n";
message += "Температура: " + String(temperature, 1) + " °C\n";
message += "Влажность воздуха: " + String(humidity, 1) + " %\n";
message += "Влажность почвы: " + String(soilPercent) + " %\n\n";
if (soilPercent < 30) {
message += "⚠️ Почва сухая — пора поливать! 🌊";
} else {
message += "✅ Влажность почвы в норме";
}
bot.sendMessage(chatId, message, "Markdown");
}