Telegram-бот на ESP32: уведомления, управление светодиодом и реле
Telegram-бот на ESP32: токен от BotFather, chat ID, настройка UniversalTelegramBot, уведомления о движении и газе, управление реле командами и ответы только тебе.
Telegram-бот на ESP32 позволяет плате ESP32 у тебя дома отправлять сообщения на телефон и выполнять команды, которые ты присылаешь в ответ. Когда у двери появляется движение или на кухне обнаружен газ, уведомление мгновенно приходит тебе в карман; напиши «/ac» — и реле включит лампу, напиши «/kapat» — и выключит. Не нужно писать своё приложение для телефона, открывать порт на роутере или платить за облачный сервис: хватит бесплатного Bot API от Telegram и встроенного Wi-Fi у ESP32.
В этом руководстве с нуля соберём рабочую систему: создадим бота через BotFather и получим его токен, узнаем твой chat ID, установим библиотеки UniversalTelegramBot и ArduinoJson и отправим первое сообщение с ESP32. Затем добавим уведомления о движении и газе с PIR-датчиком и датчиком газа MQ-2, а также управление светодиодом и реле командами Telegram. Ещё разберём, как сделать так, чтобы бот отвечал только на твой ID, настройку сертификата, отличия на ESP8266 и Raspberry Pi Pico W и самые частые ошибки по порядку.

Что такое Telegram-бот на ESP32 и как он работает?
В Telegram бот — это особый аккаунт, которым управляет программа, а не человек; в нашем проекте эту программу выполняет ESP32. ESP32 и твой телефон никогда не соединяются напрямую: оба общаются с сервером Telegram. Когда ты пишешь боту «/ac» с телефона, сообщение ждёт на сервере. Каждую секунду ESP32 отправляет на api.telegram.org зашифрованный HTTPS-запрос (getUpdates) с вопросом «есть для меня новые сообщения?», забирает сообщение и выполняет его. Чтобы отправить уведомление, она делает запрос sendMessage в обратную сторону. Поскольку соединение всегда открывает ESP32, на роутере не нужны ни проброс портов, ни статический IP; плата работает в любой сети Wi-Fi 2,4 GHz с выходом в интернет.
| Понятие | Для чего нужно | Пример |
|---|---|---|
| BotFather | Бот Telegram для создания ботов и управления ими | /newbot, /setcommands, /revoke (команды BotFather) |
| Токен бота | Пароль бота; с ним твой код подключается к Telegram | 1234567890:AAE... (число, двоеточие, длинная строка) |
| Chat ID (ID чата) | Номер человека или группы, куда уходит сообщение | Человек: 123456789, группа: -1001234567890 |
| getUpdates | Забирает с сервера новые сообщения, отправленные боту | bot.getUpdates(...) |
| sendMessage | Отправляет сообщение на указанный chat ID | bot.sendMessage(CHAT_ID, "Привет") |
| Команда | Короткое сообщение, начинающееся с /, которое понимает бот | /ac, /kapat, /durum |
Почему Telegram? Сравнение с другими способами получать уведомления
| Способ | Плюсы | Минусы | Лучше всего для |
|---|---|---|---|
| Telegram-бот | Бесплатно, мгновенные уведомления, команды в обе стороны, не нужно открывать порты | Нет графиков и готовой панели | Сигнализация и простое дистанционное управление |
| Blynk IoT | Готовая панель с кнопками, индикаторами и графиками | Ограничения на сообщения и устройства в бесплатном плане | Панели мониторинга датчиков |
| Электронная почта (SMTP) | Работает на любом телефоне, подходит для длинных отчётов | Задержки, сложно принимать команды | Ежедневные отчёты |
| SMS (SIM800L) | Не нужны ни Wi-Fi, ни интернет | SIM-карта и плата за SMS, дополнительный модуль | Дачи и сараи без интернета |
| Веб-сервер на ESP32 | Полностью локально и бесплатно | Обычно доступен только из той же сети, уведомлений нет | Панель управления внутри дома |
Короче говоря, Telegram — самый простой и дешёвый вариант для проектов в духе «сообщи мне, когда что-то произойдёт, и сделай вот это, когда я пришлю команду». Если хочешь следить за данными датчиков на графиках, тебе может больше подойти руководство по Blynk и ESP32; если уведомления нужны из места без Wi-Fi, загляни в руководство по GSM-модулю SIM800L.
Шаг 1: создай бота через BotFather и получи токен
- Открой Telegram на телефоне или компьютере, введи BotFather в строку поиска и выбери официальный аккаунт с синей галочкой рядом с именем. Остерегайся поддельных аккаунтов с похожими именами.
- Нажми Start («Запустить») и отправь команду /newbot.
- Сначала BotFather спросит отображаемое имя бота; введи любое, например «Домашняя сигнализация».
- Затем он попросит имя пользователя. Оно должно быть уникальным, состоять только из латинских букв, цифр и подчёркиваний и заканчиваться на bot, например my_home_alarm_bot. Если имя занято, добавь в конце цифру и попробуй ещё раз.
- BotFather ответит сообщением «Done! Congratulations on your new bot.» (готово, поздравляем с новым ботом), в котором будут ссылка на бота и его токен. Токен выглядит как 1234567890:AAE...: число, двоеточие и длинная строка символов. Скопируй его целиком и сохрани в надёжном месте.
- Открой своего бота по ссылке от BotFather и нажми Start. Этот шаг обязателен: в Telegram бот не может первым написать пользователю, поэтому разговор всегда начинаешь ты.
Шаг 2: как узнать свой chat ID в Telegram
Токен бота отвечает на вопрос «какой бот?», а chat ID — на вопрос «кому идёт сообщение?». У каждого пользователя, группы и канала Telegram есть постоянный числовой ID, и это не то же самое, что имя пользователя, начинающееся с @. Есть три простых способа узнать свой chat ID:
- Через ID-бота: найди в Telegram ID-бота, например @userinfobot, и нажми «Запустить» (Start); он пришлёт твой номер в строке Id. Эти боты не твои, они только показывают твой ID.
- Через браузер: сначала отправь любое сообщение своему боту. Затем открой в браузере на компьютере api.telegram.org/botTOKEN/getUpdates (вставь свой токен вместо TOKEN, а слово «bot» перед ним оставь). В появившемся тексте число в "chat":{"id":123456789 и есть твой chat ID. Если страница показывает пустой список result, напиши боту ещё раз и обнови страницу.
- Через ESP32: после загрузки кода с командами (он ниже) ID каждого, у кого нет доступа, выводится в монитор порта в строке «unauthorized ID» (неавторизованный ID). Когда захочешь добавить члена семьи, попроси его написать боту и посмотри его номер там.
Шаг 3: установи UniversalTelegramBot и ArduinoJson
- Если пакет плат ESP32 ещё не установлен в Arduino IDE, сначала установи его; шаги описаны в статье Что такое ESP32?.
- Открой «Инструменты › Управлять библиотеками» (Tools › Manage Libraries), введи UniversalTelegramBot в строку поиска и установи библиотеку от автора Brian Lough. Код из этого руководства скомпилирован с UniversalTelegramBot 1.3.0, ArduinoJson 7 и пакетом плат ESP32 версии 2.0.11.
- Если установщик спросит про зависимости, выбери Установить все (Install all); если не спросит, найди в том же окне ArduinoJson и отдельно установи библиотеку Benoit Blanchon.
- В меню «Файл › Примеры › UniversalTelegramBot › ESP32» есть готовые примеры вроде FlashLED; код из этого руководства — их упрощённая версия с добавленной защитой.
UniversalTelegramBot написана в стиле ArduinoJson 6-й версии, но без проблем компилируется и с ArduinoJson 7. Если в Arduino IDE включены предупреждения компилятора, появятся строки вроде «'DynamicJsonDocument' is deprecated: use JsonDocument instead» (класс устарел, используй JsonDocument); это предупреждения, а не ошибки, и программа работает нормально. Если при установке библиотек что-то не получается, загляни в руководство Как установить библиотеку Arduino.
Компоненты и подключение
Для полного проекта понадобятся отладочная плата ESP32, PIR-датчик движения HC-SR501, модуль датчика газа MQ-2, модуль реле на 5 V, по одному резистору на 10 kΩ и 20 kΩ, макетная плата и провода-перемычки. В качестве светодиода используем встроенный синий светодиод ESP32 на GPIO 2. Чтобы попробовать первые два шага, хватит только ESP32 и USB-кабеля; датчики можно добавить позже.




| Подключение | ESP32 | ESP8266 (NodeMCU) | Raspberry Pi Pico W |
|---|---|---|---|
| PIR VCC | VIN (5V) | VIN (5V) | VBUS (5V) |
| PIR OUT | GPIO 27 | D5 (GPIO 14) | GP16 |
| MQ-2 VCC | VIN (5V) | VIN (5V) | VBUS (5V) |
| MQ-2 AO (через делитель 10 kΩ / 20 kΩ) | GPIO 34 | A0 | GP26 (ADC0) |
| Реле VCC | VIN (5V) | VIN (5V) | VBUS (5V) |
| Реле IN | GPIO 26 | D1 (GPIO 5) | GP15 |
| Светодиод | GPIO 2 (встроенный) | D4 (встроенный, горит при LOW) | «LED» (встроенный) |
| Все GND | GND | GND | GND |
Удели внимание и питанию: один только нагреватель MQ-2 потребляет около 150 mA, катушка реле — около 70 mA, а ESP32 при передаче по Wi-Fi может кратковременно потреблять до 250 mA. USB-порт компьютера не всегда выдаёт такой суммарный ток, и плата может перезагрузиться как раз в момент отправки сообщения. Используй блок питания 5 V не меньше чем на 1 A и хороший кабель; если проблема не уходит, прочитай статью Brownout и Guru Meditation на ESP32. Все пины своей платы можно посмотреть в утилите распиновки.
Первый тест: отправляем сообщение с ESP32 в Telegram
Прежде чем переходить к датчикам, проверим токен и chat ID самым простым скетчем. В коде ниже замени строки с именем сети Wi-Fi, паролем, BOT_TOKEN и CHAT_ID своими данными и загрузи скетч в плату. Затем открой монитор порта на скорости 115200 бод:
1#include <WiFi.h>
2#include <WiFiClientSecure.h>
3#include <UniversalTelegramBot.h>
4#include <ArduinoJson.h>
5
6const char* WIFI_SSID = "AgAdi"; // Wi-Fi adın (yalnız 2,4 GHz) / your Wi-Fi name (2.4 GHz only)
7const char* WIFI_PASS = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
8#define BOT_TOKEN "1234567890:AAExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // BotFather'ın verdiği token / token from BotFather
9#define CHAT_ID "123456789" // kendi Telegram ID'n / your own Telegram ID
10
11WiFiClientSecure client; // şifreli (HTTPS) bağlantı / encrypted (HTTPS) connection
12UniversalTelegramBot bot(BOT_TOKEN, client);
13
14void setup() {
15 Serial.begin(115200);
16 WiFi.mode(WIFI_STA);
17 WiFi.begin(WIFI_SSID, WIFI_PASS);
18 Serial.print("Wi-Fi'ye baglaniyor / connecting to Wi-Fi");
19 while (WiFi.status() != WL_CONNECTED) {
20 delay(500);
21 Serial.print(".");
22 }
23 Serial.println();
24 Serial.println(WiFi.localIP());
25
26 client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // api.telegram.org kök sertifikası / root certificate of api.telegram.org
27
28 if (bot.sendMessage(CHAT_ID, "Merhaba! ESP32 çevrimiçi / Hello! ESP32 is online")) {
29 Serial.println("Mesaj gonderildi / message sent");
30 } else {
31 Serial.println("Mesaj gonderilemedi / message failed");
32 }
33}
34
35void loop() {
36}Через несколько секунд бот должен прислать тебе в Telegram «Hello! ESP32 is online», а в мониторе порта должно появиться «Mesaj gonderildi / message sent». sendMessage() возвращает true, когда сообщение доставлено; если видишь «message failed», проблема в подключении к Wi-Fi, токене, chat ID или настройке сертификата. Прежде чем винить код, открой в браузере api.telegram.org/botTOKEN/getMe: при правильном токене появятся "ok":true и имя бота, при неправильном — «Unauthorized».
Настройка сертификата: setCACert или setInsecure?
Telegram принимает только HTTPS, поэтому в коде используется WiFiClientSecure вместо обычного WiFiClient. При зашифрованном соединении ESP32 должна убедиться, что сервер на другом конце действительно api.telegram.org; для этого она проверяет сертификат сервера по корневому сертификату на вершине цепочки. UniversalTelegramBot поставляется с этим корневым сертификатом, готовым к использованию, под именем TELEGRAM_CERTIFICATE_ROOT. Цепочка сертификатов Telegram ведёт к корневому центру Go Daddy Root Certificate Authority - G2, который действует до конца 2037 года; если Telegram когда-нибудь сменит поставщика сертификатов, библиотеку тоже придётся обновить.
| Настройка | Что делает | Безопасность | Когда использовать |
|---|---|---|---|
| client.setCACert(TELEGRAM_CERTIFICATE_ROOT) | Шифрует соединение и проверяет подлинность сервера | Высокая | Всегда: это рекомендуемая настройка |
| client.setInsecure() | Шифрует соединение, но не проверяет подлинность сервера | Средняя: не заметит поддельный сервер посередине | Быстрый тест, чтобы исключить проблемы с сертификатом |
| Ни одной из этих строк | В актуальных ядрах ESP32 соединение вообще не устанавливается | Не работает | Никогда: сообщение не уходит, а в мониторе порта нет никакой ошибки |
| ESP8266: setTrustAnchors(&cert) | Аналог для ESP8266; сертификат передаётся как X509List | Высокая | NodeMCU и Wemos D1 Mini; нужно правильное время |
Управление светодиодом и реле командами Telegram (/ac, /kapat)
Теперь сделаем бота двусторонним. Код ниже раз в секунду спрашивает у Telegram новые сообщения; если текст — /ac (по-турецки «вкл»), он включает реле и светодиод, /kapat («выкл») выключает их, /durum («состояние») сообщает текущее состояние, а /start присылает список команд. Команды можно переименовать в /on и /off, но учти: команды Telegram могут содержать только строчные латинские буквы, цифры и подчёркивания и быть длиной не больше 32 символов. Команда с буквами с диакритикой, например «/aç», не распознаётся как команда, на которую можно нажать.
1#include <WiFi.h>
2#include <WiFiClientSecure.h>
3#include <UniversalTelegramBot.h>
4#include <ArduinoJson.h>
5
6const char* WIFI_SSID = "AgAdi";
7const char* WIFI_PASS = "Sifre123";
8#define BOT_TOKEN "1234567890:AAExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
9#define CHAT_ID "123456789" // yalnız bu ID'ye yanıt verilir / only this ID gets answers
10
11const int LED_PIN = 2; // çoğu DevKit'te dahili LED / onboard LED on most DevKits
12const int ROLE_PIN = 26; // röle modülünün IN pini / relay module IN pin
13const bool ROLE_AKTIF_LOW = true; // çoğu modül LOW'da çeker / most modules switch on with LOW
14
15const unsigned long KONTROL_ARALIGI = 1000; // Telegram'a saniyede bir sor / ask Telegram once per second
16unsigned long sonKontrol = 0;
17bool roleAcik = false;
18
19WiFiClientSecure client;
20UniversalTelegramBot bot(BOT_TOKEN, client);
21
22void roleYaz(bool acik) {
23 roleAcik = acik;
24 if (ROLE_AKTIF_LOW) digitalWrite(ROLE_PIN, acik ? LOW : HIGH);
25 else digitalWrite(ROLE_PIN, acik ? HIGH : LOW);
26 digitalWrite(LED_PIN, acik ? HIGH : LOW); // LED röle durumunu gösterir / LED mirrors the relay
27}
28
29void mesajlariIsle(int adet) {
30 for (int i = 0; i < adet; i++) {
31 String chatId = bot.messages[i].chat_id;
32 String metin = bot.messages[i].text;
33
34 // Güvenlik: tanımadığın kişiye cevap verme / security: never answer strangers
35 if (chatId != CHAT_ID) {
36 Serial.println("Yetkisiz ID / unauthorized ID: " + chatId);
37 continue;
38 }
39
40 int at = metin.indexOf('@'); // gruptaki "/ac@bot_adi" -> "/ac" / "/ac@bot_name" in groups -> "/ac"
41 if (at > 0) metin = metin.substring(0, at);
42
43 if (metin == "/start") {
44 String yardim = "Merhaba " + bot.messages[i].from_name + "!\n";
45 yardim += "/ac - röleyi aç / relay ON\n";
46 yardim += "/kapat - röleyi kapat / relay OFF\n";
47 yardim += "/durum - son durum / status";
48 bot.sendMessage(chatId, yardim);
49 } else if (metin == "/ac") {
50 roleYaz(true);
51 bot.sendMessage(chatId, "Röle AÇIK / relay ON");
52 } else if (metin == "/kapat") {
53 roleYaz(false);
54 bot.sendMessage(chatId, "Röle KAPALI / relay OFF");
55 } else if (metin == "/durum") {
56 bot.sendMessage(chatId, roleAcik ? "Röle AÇIK / relay ON" : "Röle KAPALI / relay OFF");
57 } else {
58 bot.sendMessage(chatId, "Bilinmeyen komut, /start yaz / unknown command, send /start");
59 }
60 }
61}
62
63void setup() {
64 Serial.begin(115200);
65 pinMode(LED_PIN, OUTPUT);
66 pinMode(ROLE_PIN, OUTPUT);
67 roleYaz(false); // açılışta röle kapalı / relay off at boot
68
69 WiFi.mode(WIFI_STA);
70 WiFi.begin(WIFI_SSID, WIFI_PASS);
71 while (WiFi.status() != WL_CONNECTED) delay(500);
72 Serial.println(WiFi.localIP());
73
74 client.setCACert(TELEGRAM_CERTIFICATE_ROOT);
75 bot.sendMessage(CHAT_ID, "Bot hazır, /start yaz / bot ready, send /start");
76}
77
78void loop() {
79 if (millis() - sonKontrol > KONTROL_ARALIGI) {
80 int adet = bot.getUpdates(bot.last_message_received + 1); // yeni mesaj var mı? / any new messages?
81 while (adet) {
82 mesajlariIsle(adet);
83 adet = bot.getUpdates(bot.last_message_received + 1);
84 }
85 sonKontrol = millis();
86 }
87}| Строка | Что делает |
|---|---|
| bot.getUpdates(bot.last_message_received + 1) | Запрашивает сообщения после последнего обработанного и возвращает, сколько их пришло; +1 не даёт тому же сообщению прийти снова |
| bot.messages[i].chat_id | ID чата, из которого пришло сообщение; туда уходит ответ, и по нему идёт проверка безопасности |
| bot.messages[i].text (текст сообщения) | Входящий текст; с ним сравнивается команда |
| metin.indexOf('@') | Отрезает часть @bot_name, которую Telegram добавляет к командам в группах |
| ROLE_AKTIF_LOW | Большинство модулей реле включаются сигналом LOW; если твой модуль включается сигналом HIGH, поставь false |
| KONTROL_ARALIGI | Частота опроса; при 1000 мс реакция быстрая, при 3000–5000 мс нагрузка на сеть меньше |
Безопасность: пусть бот отвечает только на твой ID
Имена ботов в Telegram публичны, и их можно найти поиском. Если кто-то угадает my_home_alarm_bot или увидит его на скриншоте и отправит ему /ac, ESP32 без проверки выполнит и эту команду. Поэтому самая важная строка в коде — проверка chatId != CHAT_ID. Твой chat ID не обязан быть секретным: защита держится на том, что Telegram на своей стороне проверяет, кто отправил каждое сообщение, и подделать чужой ID никто не может. Совсем не отвечать незнакомцам — тоже осознанное решение: пока бот молчит, никто даже не поймёт, что он работает.
- Используй белый список: храни разрешённые ID в массиве, игнорируй все остальные сообщения и добавляй членов семьи по одному.
- Проверяй человека в группах: в группе chat ID — это ID группы; если команды должны отправлять только определённые люди, сравнивай from_id, а не chat_id.
- Храни токен в отдельном файле: вынеси токен и пароль Wi-Fi в файл вроде secrets.h и не прикладывай его, когда делишься кодом.
- Пропускай старые команды: /ac, отправленная во время отключения электричества, не должна сработать в неожиданный момент, когда плата снова включится; об этом заботится очистка при запуске в полном коде.
- Безопасное состояние при запуске: при каждой загрузке платы начинай с выключенного реле; для таких нагрузок, как обогреватель или насос, это очень важно.
- Не доверяй критичные задачи только боту: для дверного замка или котла подумай, к чему может привести ошибочная команда, и добавь подтверждающее сообщение или ограничение по времени.
Сигнализация движения и газа: полный проект с уведомлениями в Telegram
Теперь соберём всё вместе. Код ниже присылает тебе уведомление в Telegram с отметкой времени, когда PIR-датчик замечает движение и когда показание газа MQ-2 превышает порог. Одновременно он слушает команды /ac, /kapat, /alarm_ac, /alarm_kapat и /durum, по команде /start показывает внизу экрана меню из кнопок, которые отправляют команду одним нажатием, и добавляет команды в кнопку «Меню» Telegram. Подробности о каждом датчике — в руководствах по PIR-датчику движения и датчику газа MQ-2.
1#include <WiFi.h>
2#include <WiFiClientSecure.h>
3#include <UniversalTelegramBot.h>
4#include <ArduinoJson.h>
5#include <time.h>
6
7const char* WIFI_SSID = "AgAdi";
8const char* WIFI_PASS = "Sifre123";
9#define BOT_TOKEN "1234567890:AAExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
10#define CHAT_ID "123456789" // bildirimlerin gideceği ID / the ID that receives alerts
11
12// Komut verebilecek kişiler; aile üyesi eklemek için: { CHAT_ID, "222222222" }
13// People allowed to send commands; to add a family member: { CHAT_ID, "222222222" }
14const char* YETKILI[] = { CHAT_ID };
15const int YETKILI_SAYISI = sizeof(YETKILI) / sizeof(YETKILI[0]);
16
17const int LED_PIN = 2; // dahili LED / onboard LED
18const int ROLE_PIN = 26; // röle modülü IN / relay module IN
19const int PIR_PIN = 27; // PIR sensörü OUT / PIR sensor OUT
20const int GAZ_PIN = 34; // MQ-2 AO, gerilim bölücü üzerinden / MQ-2 AO through a voltage divider
21const bool ROLE_AKTIF_LOW = true; // çoğu modül LOW'da çeker / most modules switch on with LOW
22
23const int GAZ_ESIK = 1800; // temiz havadaki değere göre ayarla / tune it from your clean-air reading
24const int GAZ_FARK = 200; // histerezis payı / hysteresis margin
25const unsigned long HAREKET_BEKLEME = 60000; // iki hareket bildirimi arası en az 60 sn / at least 60 s between motion alerts
26const unsigned long GAZ_ISINMA = 120000; // MQ-2 ilk 2 dakika ısınır / MQ-2 warms up for the first 2 minutes
27const unsigned long KONTROL_ARALIGI = 1000; // Telegram'a saniyede bir sor / poll Telegram once per second
28
29WiFiClientSecure client;
30UniversalTelegramBot bot(BOT_TOKEN, client);
31
32bool alarmKurulu = true; // hareket alarmı açık mı / is the motion alarm armed
33bool roleAcik = false;
34bool gazVar = false;
35unsigned long sonKontrol = 0;
36unsigned long sonHareket = 0;
37
38String saat() { // "14:05:31" biçiminde saat / time as "14:05:31"
39 struct tm t;
40 if (!getLocalTime(&t, 100)) return "--:--:--";
41 char buf[9];
42 strftime(buf, sizeof(buf), "%H:%M:%S", &t);
43 return String(buf);
44}
45
46bool yetkiliMi(const String& id) {
47 for (int i = 0; i < YETKILI_SAYISI; i++) {
48 if (id == YETKILI[i]) return true;
49 }
50 return false;
51}
52
53void roleYaz(bool acik) {
54 roleAcik = acik;
55 if (ROLE_AKTIF_LOW) digitalWrite(ROLE_PIN, acik ? LOW : HIGH);
56 else digitalWrite(ROLE_PIN, acik ? HIGH : LOW);
57}
58
59String durumMetni() {
60 String s = "Röle / relay: ";
61 s += roleAcik ? "AÇIK / ON" : "KAPALI / OFF";
62 s += "\nAlarm: ";
63 s += alarmKurulu ? "KURULU / ARMED" : "KAPALI / DISARMED";
64 s += "\nGaz / gas: " + String(analogRead(GAZ_PIN));
65 s += "\nWi-Fi: " + String(WiFi.RSSI()) + " dBm";
66 return s;
67}
68
69void mesajlariIsle(int adet) {
70 for (int i = 0; i < adet; i++) {
71 String chatId = bot.messages[i].chat_id;
72 String metin = bot.messages[i].text;
73
74 if (!yetkiliMi(chatId)) { // yabancıya sessiz kal / stay silent to strangers
75 Serial.println("Yetkisiz ID / unauthorized ID: " + chatId);
76 continue;
77 }
78 int at = metin.indexOf('@'); // "/ac@bot_adi" -> "/ac"
79 if (at > 0) metin = metin.substring(0, at);
80
81 if (metin == "/ac") {
82 roleYaz(true);
83 bot.sendMessage(chatId, "Röle AÇIK / relay ON");
84 } else if (metin == "/kapat") {
85 roleYaz(false);
86 bot.sendMessage(chatId, "Röle KAPALI / relay OFF");
87 } else if (metin == "/alarm_ac") {
88 alarmKurulu = true;
89 bot.sendMessage(chatId, "Hareket alarmı kuruldu / motion alarm armed");
90 } else if (metin == "/alarm_kapat") {
91 alarmKurulu = false;
92 bot.sendMessage(chatId, "Hareket alarmı kapatıldı / motion alarm disarmed");
93 } else if (metin == "/durum") {
94 bot.sendMessage(chatId, durumMetni());
95 } else { // /start ve diğer her şey: tuş menüsü / /start and anything else: button menu
96 String klavye = "[[\"/ac\",\"/kapat\"],[\"/alarm_ac\",\"/alarm_kapat\"],[\"/durum\"]]";
97 bot.sendMessageWithReplyKeyboard(chatId, "Komut seç / choose a command", "", klavye, true);
98 }
99 }
100}
101
102void setup() {
103 Serial.begin(115200);
104 pinMode(LED_PIN, OUTPUT);
105 pinMode(ROLE_PIN, OUTPUT);
106 pinMode(PIR_PIN, INPUT);
107 roleYaz(false); // açılışta röle kapalı / relay off at boot
108
109 WiFi.mode(WIFI_STA);
110 WiFi.begin(WIFI_SSID, WIFI_PASS);
111 while (WiFi.status() != WL_CONNECTED) {
112 delay(500);
113 Serial.print(".");
114 }
115 Serial.println(WiFi.localIP());
116
117 client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // api.telegram.org kök sertifikası / root certificate of api.telegram.org
118 bot.maxMessageLength = 4000; // büyük mesajlarda takılmayı önler / prevents hanging on big messages
119 configTime(3 * 3600, 0, "pool.ntp.org"); // Türkiye UTC+3; kendi saat dilimini yaz / Turkey is UTC+3, set your own zone
120
121 // Telegram'daki Menü düğmesi; komutlar küçük harf, Türkçe karakter yok
122 // The Menu button in Telegram; commands are lowercase with no special letters
123 bot.setMyCommands("[{\"command\":\"ac\",\"description\":\"Röleyi aç\"},"
124 "{\"command\":\"kapat\",\"description\":\"Röleyi kapat\"},"
125 "{\"command\":\"alarm_ac\",\"description\":\"Hareket alarmını kur\"},"
126 "{\"command\":\"alarm_kapat\",\"description\":\"Hareket alarmını kapat\"},"
127 "{\"command\":\"durum\",\"description\":\"Durumu göster\"}]");
128
129 // Kart kapalıyken gelen eski komutları atla / skip old commands sent while the board was off
130 while (bot.getUpdates(bot.last_message_received + 1)) { }
131
132 bot.sendMessage(CHAT_ID, "Sistem açıldı / system started " + saat());
133}
134
135void loop() {
136 unsigned long simdi = millis();
137
138 // 1) Hareket alarmı / motion alarm
139 if (alarmKurulu && digitalRead(PIR_PIN) == HIGH && simdi - sonHareket > HAREKET_BEKLEME) {
140 sonHareket = simdi;
141 digitalWrite(LED_PIN, HIGH);
142 bot.sendMessage(CHAT_ID, "Hareket algılandı! / motion detected! " + saat());
143 digitalWrite(LED_PIN, LOW);
144 }
145
146 // 2) Gaz alarmı: ısınmadan sonra, susturulamaz / gas alarm: after warm-up, cannot be muted
147 if (simdi > GAZ_ISINMA) {
148 int gaz = analogRead(GAZ_PIN); // 0-4095
149 if (!gazVar && gaz > GAZ_ESIK) {
150 gazVar = true;
151 bot.sendMessage(CHAT_ID, "GAZ ALARMI! / GAS ALARM! " + String(gaz) + " " + saat());
152 } else if (gazVar && gaz < GAZ_ESIK - GAZ_FARK) {
153 gazVar = false;
154 bot.sendMessage(CHAT_ID, "Gaz normale döndü / gas back to normal: " + String(gaz));
155 }
156 }
157
158 // 3) Telegram komutları / Telegram commands
159 if (simdi - sonKontrol > KONTROL_ARALIGI && WiFi.status() == WL_CONNECTED) {
160 int adet = bot.getUpdates(bot.last_message_received + 1);
161 while (adet) {
162 mesajlariIsle(adet);
163 adet = bot.getUpdates(bot.last_message_received + 1);
164 }
165 sonKontrol = millis();
166 }
167}| Настройка | Значение | Пояснение |
|---|---|---|
| GAZ_ESIK | 1800 | Сырое значение АЦП от 0 до 4095; подбери его по своему показанию на чистом воздухе |
| GAZ_FARK | 200 | Насколько показание должно опуститься ниже порога, прежде чем придёт сообщение «снова норма»; не даёт уведомлениям скакать туда-сюда на границе |
| HAREKET_BEKLEME | 60000 мс | Оставляет не меньше 60 секунд между двумя уведомлениями о движении |
| GAZ_ISINMA | 120000 мс | Пока MQ-2 прогревается, показания завышены; первые 2 минуты газ не проверяется |
| KONTROL_ARALIGI | 1000 мс | Как часто у Telegram запрашиваются новые сообщения |
| bot.maxMessageLength | 4000 | Самый длинный ответ, который читает библиотека; по умолчанию 1500 |
Две сигнализации в коде намеренно работают по-разному. Сигнализацию движения защищает пауза между уведомлениями (cooldown): PIR-датчик срабатывает снова и снова, пока кто-то ходит по комнате, и без паузы телефон заполнился бы десятками сообщений в минуту. Поскольку таймер паузы стартует с нуля, первое уведомление может прийти не раньше чем через 60 секунд после запуска платы, и это как раз совпадает с примерно одной минутой, которая нужна PIR-датчику, чтобы стабилизироваться после включения. Газовая сигнализация вместо этого использует гистерезис: она предупреждает один раз, когда показание превышает порог, сообщает «снова норма», только когда оно опустится на 200 единиц ниже порога, и в целях безопасности её нельзя заглушить командой /alarm_kapat. Telegram тоже не разрешает слишком частые сообщения: если пытаться отправить больше одного сообщения в секунду в один чат или больше 20 в минуту в группу, придёт ошибка «Too Many Requests: retry after» (слишком много запросов).
Telegram-бот на ESP8266 (NodeMCU)
Та же библиотека работает и на платах ESP8266 NodeMCU и Wemos D1 Mini; логика команд и сигнализации точно такая же, меняется только часть с подключением. Вместо WiFi.h используется ESP8266WiFi.h, корневой сертификат передаётся как X509List в setTrustAnchors(), а поскольку ESP8266 проверяет сроки действия сертификата, перед подключением нужно получить время по NTP. Без времени каждое подключение молча завершается неудачей. Встроенный светодиод NodeMCU находится на пине D4 и горит при LOW, а аналоговый вход всего один — A0.
1#include <ESP8266WiFi.h>
2#include <WiFiClientSecure.h>
3#include <UniversalTelegramBot.h>
4#include <ArduinoJson.h>
5#include <time.h>
6
7const char* WIFI_SSID = "AgAdi";
8const char* WIFI_PASS = "Sifre123";
9#define BOT_TOKEN "1234567890:AAExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
10#define CHAT_ID "123456789"
11
12X509List cert(TELEGRAM_CERTIFICATE_ROOT); // ESP8266'da kök sertifika böyle verilir / how ESP8266 takes the root certificate
13WiFiClientSecure client;
14UniversalTelegramBot bot(BOT_TOKEN, client);
15
16void setup() {
17 Serial.begin(115200);
18 configTime(0, 0, "pool.ntp.org"); // sertifika kontrolü için saat şart / the clock is needed to check the certificate
19 client.setTrustAnchors(&cert);
20
21 WiFi.mode(WIFI_STA);
22 WiFi.begin(WIFI_SSID, WIFI_PASS);
23 while (WiFi.status() != WL_CONNECTED) delay(500);
24
25 time_t simdi = time(nullptr); // NTP saatini bekle / wait for the NTP time
26 while (simdi < 24 * 3600) {
27 delay(100);
28 simdi = time(nullptr);
29 }
30 bot.sendMessage(CHAT_ID, "ESP8266 çevrimiçi / ESP8266 is online");
31}
32
33void loop() {
34}Telegram-бот на Raspberry Pi Pico W и MicroPython
Для Raspberry Pi Pico W отдельная библиотека не нужна: запросы к Telegram Bot API можно отправлять напрямую модулем requests из MicroPython. Код ниже присылает уведомление при обнаружении движения, переключает реле на GP15 и встроенный светодиод платы командами /ac и /kapat и отвечает только на твой chat ID. Установи на Pico W свежую версию MicroPython через Thonny и сохрани код как main.py. Модуль requests в MicroPython шифрует соединение, но не проверяет сертификат сервера, поэтому уровень безопасности такой же, как у setInsecure() на ESP32.
1import network, time
2import requests
3from machine import Pin
4
5WIFI_SSID = "AgAdi" # Wi-Fi adın / your Wi-Fi name
6WIFI_PASS = "Sifre123" # Wi-Fi şifren / your Wi-Fi password
7TOKEN = "1234567890:AAExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
8CHAT_ID = 123456789 # sayı olarak yaz / write it as a number
9API = "https://api.telegram.org/bot" + TOKEN
10
11led = Pin("LED", Pin.OUT) # Pico W dahili LED / Pico W onboard LED
12role = Pin(15, Pin.OUT, value=1) # aktif LOW röle kapalı başlar / active-LOW relay starts off
13pir = Pin(16, Pin.IN) # PIR OUT
14
15wlan = network.WLAN(network.STA_IF)
16wlan.active(True)
17wlan.connect(WIFI_SSID, WIFI_PASS)
18while not wlan.isconnected():
19 time.sleep(0.5)
20print("IP:", wlan.ifconfig()[0])
21
22def gonder(metin):
23 try:
24 r = requests.post(API + "/sendMessage", json={"chat_id": CHAT_ID, "text": metin})
25 r.close()
26 except Exception as e:
27 print("Gonderilemedi / send failed:", e)
28
29def yeni_mesajlar(offset):
30 try:
31 r = requests.get(API + "/getUpdates?timeout=0&limit=5&offset=%d" % offset)
32 sonuc = r.json().get("result", [])
33 r.close()
34 return sonuc
35 except Exception as e:
36 print("Okunamadi / read failed:", e)
37 return []
38
39# Kart kapalıyken gelen eski komutları atla / skip old commands sent while the board was off
40offset = 0
41eski = yeni_mesajlar(offset)
42while eski:
43 offset = eski[-1]["update_id"] + 1
44 eski = yeni_mesajlar(offset)
45
46gonder("Pico W çevrimiçi / Pico W is online")
47son_hareket = time.ticks_ms() # ilk 60 sn PIR dengelenir / the PIR settles in the first 60 s
48
49while True:
50 if pir.value() and time.ticks_diff(time.ticks_ms(), son_hareket) > 60000:
51 son_hareket = time.ticks_ms()
52 gonder("Hareket algılandı! / motion detected!")
53
54 for u in yeni_mesajlar(offset):
55 offset = u["update_id"] + 1
56 mesaj = u.get("message", {})
57 if mesaj.get("chat", {}).get("id") != CHAT_ID: # yalnız sana yanıt ver / answer only you
58 continue
59 komut = mesaj.get("text", "").split("@")[0]
60 if komut == "/ac":
61 role.value(0) # aktif LOW: 0 = açık / active LOW: 0 = on
62 led.on()
63 gonder("Röle AÇIK / relay ON")
64 elif komut == "/kapat":
65 role.value(1)
66 led.off()
67 gonder("Röle KAPALI / relay OFF")
68 elif komut == "/durum":
69 gonder("Röle / relay: " + ("AÇIK / ON" if role.value() == 0 else "KAPALI / OFF"))
70 time.sleep(1)Идеи проектов с Telegram-ботом на ESP32
- Сигнализация для двери и окна: с герконом или PIR-датчиком отправляй сообщение на телефон, когда дома никого нет, а движение есть.
- Камера наблюдения с фото: при обнаружении движения делай снимок на ESP32-CAM и отправляй его в Telegram; шаги — в руководстве ESP32-CAM и Telegram.
- Мониторинг комнаты и теплицы: получай предупреждение, когда температура, измеренная DHT11, выходит за предел, и запрашивай текущее показание командой /durum.
- Напоминание о поливе растений: получай уведомление, когда датчик влажности почвы показывает, что земля сухая, и включай насос на несколько секунд командой вроде /sula («полить»).
- Предупреждение о пожаре: объедини датчик пламени с MQ-2, чтобы следить сразу за двумя опасностями.
- Датчик почтового ящика на батарейках: держи ESP32 в глубоком сне, буди её при открытии крышки, отправляй одно сообщение и снова усыпляй.
- Удалённая розетка: включай и выключай настольную лампу или вентилятор через реле из любой точки мира командами /ac и /kapat.
Частые ошибки Telegram-бота на ESP32 и их решение
| Симптом или сообщение | Причина | Решение |
|---|---|---|
| sendMessage возвращает false, в мониторе порта ошибок нет | Нет строки setCACert или setInsecure, в токене есть пробел или Wi-Fi не подключён | Добавь строку сертификата, заново скопируй токен, сначала проверь подключение к Wi-Fi |
| «Forbidden: bot can't initiate conversation with a user» (бот не может начать диалог) при проверке в браузере | В боте ни разу не нажата кнопка «Запустить» (Start), или бот заблокирован | Открой бота в Telegram и нажми «Запустить»; если бот заблокирован, разблокируй его |
| «Bad Request: chat not found» (чат не найден) | Неверный chat ID, пропущен минус в ID группы или бот удалён из группы | Заново посмотри ID через getUpdates и пиши ID групп со знаком минус |
| «Unauthorized» (нет авторизации) | Токен неверный или заменён через /revoke | Скопируй актуальный токен из BotFather и заново загрузи код |
| Отправляет сообщения, но не реагирует на команды | Долгая delay() в loop(), неверный chat ID или @bot_name, добавленный к команде в группе | Используй millis() для ожидания, отрежь часть с @, проверь строку «unauthorized ID» в мониторе порта |
| Бот совсем перестаёт отвечать после фото или стикера | Библиотека читает только первые 1500 символов ответа; длинный ответ не разбирается, и одно и то же сообщение приходит снова и снова | Добавь в setup() строку bot.maxMessageLength = 4000; |
| Плата при запуске выполняет старые команды | Telegram хранит непрочитанные сообщения до 24 часов | При запуске прочитай и отбрось ожидающие сообщения |
| «Conflict: terminated by other getUpdates request» (конфликт с другим запросом getUpdates) | Один и тот же токен работает одновременно на двух устройствах или в скрипте на компьютере | Создай в BotFather отдельного бота для каждого устройства |
| «Too Many Requests: retry after 30» (слишком много запросов) | Сообщения отправляются слишком часто | Добавь паузу; отправляй в один чат не больше одного сообщения в секунду |
| «ArduinoJson.h: No such file or directory» (файл не найден) при компиляции | Библиотека ArduinoJson не установлена | Установи ArduinoJson через «Менеджер библиотек» (Library Manager) |
| Плата перезагружается при отправке сообщения | Источник питания не справляется с пиком тока при передаче по Wi-Fi (просадка питания, brownout) | Блок питания на 1 A, короткий качественный кабель и конденсатор 100–470 µF между 3V3 и GND |
С токеном, chat ID и несколькими строками кода твоя ESP32 превращается в маленького помощника, который сообщает, что происходит дома, и выполняет твои команды. Эту же схему можно расширить датчиками температуры, уровня воды или двери и направлять уведомления в семейную группу. В приложении BlueGrays есть охранная система на ESP32 с PIR-датчиком, зуммером и светодиодом, которая отправляет уведомления в Telegram, сигнализация газа и дыма на MQ-2 и проекты ESP32 с Wi-Fi — пошагово, со схемами подключения и кодом.
Частые вопросы
Telegram-бот на ESP32 бесплатный?
Да. Создание бота в Telegram, использование Bot API и отправка сообщений полностью бесплатны, и месячного лимита сообщений нет. Есть только ограничения частоты: примерно одно сообщение в секунду в один чат и 20 сообщений в минуту в группу. Для домашних проектов этого более чем достаточно.
Как узнать свой chat ID в Telegram?
Проще всего нажать «Запустить» (Start) в ID-боте вроде @userinfobot в Telegram: он пришлёт тебе твой ID. Другой способ — написать своему боту, открыть в браузере api.telegram.org/botTOKEN/getUpdates и найти значение id в разделе «chat». ID групп начинается со знака минус.
Почему ESP32 не отправляет сообщения в Telegram?
Чаще всего причина в том, что в боте ни разу не нажата кнопка «Запустить» (Start), указан неверный chat ID, в токене есть пробел или в коде нет строки setCACert или setInsecure. Токен и chat ID можно проверить независимо от кода, открыв в браузере адреса getMe и sendMessage. Также убедись, что плата подключена к сети Wi-Fi 2,4 GHz.
Может ли кто-то другой управлять моим Telegram-ботом?
Имена ботов публичны, поэтому если в коде нет проверки ID, то да: любой, кто найдёт бота, сможет отправлять ему команды. Чтобы этого не случилось, сравнивай chat ID каждого входящего сообщения со своим и игнорируй остальные: отправителя подтверждает Telegram, и подделать его нельзя. Токен тоже никому не показывай; если он утёк, замени его командой /revoke в BotFather.
Можно ли сделать Telegram-бота на ESP8266?
Да. Библиотека UniversalTelegramBot работает и на платах ESP8266, таких как NodeMCU и Wemos D1 Mini. Корневой сертификат передаётся как X509List в setTrustAnchors(), а перед подключением нужно получить время по NTP. У ESP8266 меньше памяти, поэтому с длинными сообщениями ESP32 справляется лучше.
Может ли ESP32 отправлять фото в Telegram?
Да. Функцией sendPhotoByBinary() из библиотеки можно отправить кадр, снятый ESP32-CAM, прямо в Telegram. ESP32 без камеры точно так же может отправить изображение с SD-карты, а через sendPhoto() — ссылку на изображение в интернете. Системы, которые присылают фото при обнаружении движения, — одни из самых популярных охранных проектов.
Что собрать после прочтения
Все проекты
Мигающий светодиод на ESP32
Учит базовой работе с выходами GPIO на ESP32. Эффект мигания получается включением и выключением светодиода с цифрового пина. Это ESP32-версия классического проекта мигания на Arduino UNO; базовые команды мигания в среде Arduino те же, но расположение пинов ESP32 и уровни GPIO 3,3 V отличаются от Arduino UNO.

Мигание встроенным светодиодом ESP32
Мигание встроенным синим светодиодом на ESP32 DevKit V1. Внешние компоненты не нужны! Первый проект, который стоит попробовать после покупки ESP32. Встроенный светодиод подключён к GPIO 2.

Сигнализация движения на ESP32 (PIR + зуммер + светодиод + Telegram)
Умная охранная сигнализация, которая при обнаружении движения PIR-датчиком включает зуммер и светодиод и одновременно сразу отправляет владельцу уведомление в Telegram. В ней есть 30-секундная пауза, ограничивающая частоту повторных тревог; этот механизм не проверяет срабатывания PIR и не устраняет ложные тревоги. Подходит для дома, гаража, склада или мастерской. С ESP32-CAM можно добавить уведомления с фото, а с обычной ESP32 DevKit V1 приходят текстовые.

Сигнализация газа и дыма MQ-2 на ESP32
В этом проекте модуль датчика газа MQ-2 обнаруживает горючие газы (сжиженный газ, метан, пропан) и дым, а при обнаружении пассивный зуммер играет тревогу, похожую на сирену. Цифровой пороговый выход датчика (DO) подключён к GPIO 4 у ESP32; когда газ или дым превышают порог, DO переходит в LOW, и срабатывает тревога. Чувствительность настраивается потенциометром модуля. Пассивный зуммер управляется через tone() и даёт сирену на двух частотах.

Умное освещение на ESP32 (PIR + LDR + реле + OLED)
Сделай умное освещение для коридора, ванной или кладовки на ESP32! С PIR-датчиком движения, датчиком освещённости LDR и модулем реле собирается классический сценарий «свет включается при движении в темноте»: если вокруг уже светло, движение свет НЕ включает, и днём энергия экономится. Свет горит 30 секунд; если движение продолжается, таймер сбрасывается, и свет горит дольше. OLED SSD1306 крупно показывает состояние (ON / DAYTIME / WAITING) и обратный отсчёт.

Автоматическое освещение с PIR-датчиком движения на ESP32
В этом проекте PIR-датчик движения HC-SR501 замечает, что кто-то вошёл в комнату, и автоматически включает светодиод. Время свечения отслеживается через millis(); каждое показание HIGH обновляет время последнего движения, и светодиод гаснет не раньше чем через 10 секунд после последнего HIGH. Текущий код ещё и ждёт 50 ms на каждом цикле через delay(50), поэтому цикл не полностью неблокирующий. Подходит для освещения лестниц, шкафов и ванной.

Подключение ESP32 к Wi-Fi в режиме STA (с экраном LCD)
В этом проекте ESP32 надёжно и устойчиво к сбоям подключается к домашней сети Wi-Fi в режиме станции (STA). SSID и пароль хранятся в отдельном файле secrets.h, чтобы не утекли в Git; настроено автоматическое переподключение, а уровень сигнала RSSI и IP-адрес выводятся на I2C LCD-экран. Встроенный светодиод служит индикатором состояния. Это менеджер подключения уровня реальных устройств.
Другие статьи
Приложение BlueGrays
Собери эту схему шаг за шагом в телефоне
Схема подключения, код и список деталей на одном экране. 200+ проектов на Arduino, ESP32 и Pico готовы к сборке.
Наведи камеру телефона
Наведи камеру на код — откроется магазин приложений на телефоне.
или открыть магазин

