Bot Telegram con ESP32: notifiche e controllo di LED e relè
Crea un bot Telegram con ESP32: token da BotFather, chat ID, UniversalTelegramBot, avvisi di movimento e gas, relè via comando e risposte solo al tuo ID.
Un bot Telegram con ESP32 permette alla scheda ESP32 di casa tua di mandarti messaggi sul telefono e di eseguire i comandi che le invii. Quando c'è movimento davanti alla porta o viene rilevato gas in cucina, ti arriva subito una notifica; scrivi "/ac" e un relè accende la lampada, scrivi "/kapat" e la spegne. Non devi programmare un'app tua, aprire porte sul router o pagare un servizio cloud: bastano la Bot API gratuita di Telegram e il Wi-Fi integrato dell'ESP32.
In questa guida costruirai da zero un sistema funzionante: creerai un bot con BotFather e otterrai il suo token, scoprirai il tuo chat ID, installerai le librerie UniversalTelegramBot e ArduinoJson e invierai il primo messaggio dall'ESP32. Poi aggiungerai gli avvisi di movimento e gas con un sensore PIR e un sensore di gas MQ-2, e il controllo di un LED e di un relè con i comandi di Telegram. Vedremo anche come far rispondere il bot solo al tuo ID, come funziona l'impostazione del certificato, cosa cambia su ESP8266 e Raspberry Pi Pico W e quali sono gli errori più comuni.

Cos'è un bot Telegram con ESP32 e come funziona?
Su Telegram un bot è un account speciale gestito da un programma invece che da una persona; nel nostro progetto quel programma gira sull'ESP32. L'ESP32 e il telefono non si collegano mai direttamente: entrambi parlano con il server di Telegram. Quando scrivi "/ac" al bot dal telefono, il messaggio resta in attesa sul server. Ogni secondo l'ESP32 invia una richiesta HTTPS cifrata (getUpdates) ad api.telegram.org chiedendo "ci sono messaggi nuovi per me?", preleva il messaggio e lo esegue. Per inviare un avviso fa una richiesta sendMessage nella direzione opposta. Visto che è sempre l'ESP32 ad aprire la connessione, non servono inoltro delle porte né IP statico: la scheda funziona su qualsiasi rete Wi-Fi a 2,4 GHz con accesso a internet.
| Concetto | A cosa serve? | Esempio |
|---|---|---|
| BotFather | Il bot di Telegram per creare e gestire i bot | /newbot, /setcommands, /revoke |
| Token del bot | La password del bot; il tuo codice si collega a Telegram con questa | 1234567890:AAE... (numero, due punti, stringa lunga) |
| Chat ID | Il numero della persona o del gruppo che riceve il messaggio | Persona: 123456789, gruppo: -1001234567890 |
| getUpdates | Scarica dal server i nuovi messaggi inviati al bot | bot.getUpdates(...) |
| sendMessage | Invia un messaggio al chat ID indicato | bot.sendMessage(CHAT_ID, "Ciao") |
| Comando | Messaggio breve che inizia con / e che il bot capisce | /ac, /kapat, /durum |
Perché Telegram? Confronto con gli altri modi per ricevere notifiche
| Metodo | Pro | Contro | Ideale per |
|---|---|---|---|
| Bot Telegram | Gratis, avvisi istantanei, comandi nei due sensi, nessuna porta da aprire | Niente grafici né dashboard pronta | Allarmi e controllo remoto semplice |
| Blynk IoT | Pulsanti, indicatori e grafici già pronti | Limiti di messaggi e dispositivi nel piano gratuito | Dashboard di monitoraggio dei sensori |
| E-mail (SMTP) | Funziona su ogni telefono, adatta a report lunghi | In ritardo, ricevere comandi è scomodo | Report giornalieri |
| SMS (SIM800L) | Non servono Wi-Fi né internet | Costo della SIM e degli SMS, modulo in più | Casa di campagna o magazzino senza internet |
| Web server sull'ESP32 | Completamente locale e gratuito | Di solito raggiungibile solo dalla stessa rete, niente avvisi | Pannello di controllo in casa |
In breve, Telegram è la strada più semplice ed economica per i progetti del tipo "avvisami quando succede qualcosa e fai questo quando ti mando un comando". Se vuoi seguire i dati dei sensori sui grafici, ti troverai meglio con la guida Blynk con ESP32; se ti servono avvisi da un posto senza Wi-Fi, dai un'occhiata alla guida al modulo GSM SIM800L.
Passo 1: creare il bot con BotFather e ottenere il token
- Apri Telegram sul telefono o sul computer, scrivi BotFather nella ricerca e scegli l'account ufficiale, quello con la spunta blu accanto al nome. Attento agli account falsi con nomi simili.
- Tocca Avvia (Start) e invia il comando /newbot.
- BotFather ti chiede prima il nome visualizzato del bot; scrivi quello che vuoi, per esempio "Allarme Casa".
- Poi ti chiede un nome utente. Deve essere unico, contenere solo lettere latine, numeri e trattini bassi e finire con bot, per esempio allarme_casa_bot. Se è già preso, aggiungi un numero alla fine e riprova.
- BotFather risponde con "Done! Congratulations on your new bot.", il link al tuo bot e il suo token. Il token ha la forma 1234567890:AAE...: un numero, due punti e una lunga stringa di caratteri. Copialo per intero e conservalo in un posto sicuro.
- Tocca il link inviato da BotFather per aprire il tuo bot e premi Avvia. Questo passaggio è obbligatorio: su Telegram i bot non possono mandare per primi un messaggio a un utente, quindi la conversazione devi iniziarla sempre tu.
Passo 2: come trovare il tuo chat ID di Telegram?
Il token risponde alla domanda "quale bot?", il chat ID alla domanda "chi riceve il messaggio?". Ogni utente, gruppo e canale di Telegram ha un identificativo numerico fisso, che non coincide con il tuo nome utente con la @. Ci sono tre modi semplici per scoprire il tuo chat ID:
- Con un bot di identificazione: cerca su Telegram un bot come @userinfobot e premi Avvia; ti manda il tuo numero nella riga Id. Questi bot non sono tuoi, mostrano solo il tuo ID.
- Con il browser: prima manda un messaggio qualsiasi al tuo bot. Poi apri api.telegram.org/botTOKEN/getUpdates nel browser del computer (incolla il tuo token al posto di TOKEN lasciando davanti la parola "bot"). Nel testo che compare, il numero in "chat":{"id":123456789 è il tuo chat ID. Se la pagina mostra una lista result vuota, scrivi di nuovo al bot e ricarica la pagina.
- Con l'ESP32: quando carichi il codice dei comandi qui sotto, l'ID di chiunque non sia autorizzato viene stampato nel Monitor seriale con la riga "unauthorized ID". Se vuoi aggiungere un familiare, chiedigli di scrivere al bot e leggi lì il suo numero.
Passo 3: installare UniversalTelegramBot e ArduinoJson
- Se nell'Arduino IDE non hai ancora installato il pacchetto delle schede ESP32, fallo prima; i passaggi sono in Cos'è l'ESP32?.
- Apri Strumenti > Gestisci librerie, scrivi UniversalTelegramBot nella ricerca e installa la libreria di Brian Lough. I codici di questa guida sono stati compilati con UniversalTelegramBot 1.3.0, ArduinoJson 7 e la versione 2.0.11 del pacchetto schede ESP32.
- Se l'installazione ti chiede delle dipendenze, scegli Install all; se non lo fa, cerca ArduinoJson nella stessa finestra e installa a parte la libreria di Benoit Blanchon.
- In File > Esempi > UniversalTelegramBot > ESP32 trovi esempi pronti come FlashLED; i codici di questa guida ne sono versioni semplificate con in più la sicurezza.
UniversalTelegramBot è scritta con la sintassi della versione 6 di ArduinoJson, ma si compila senza problemi anche con ArduinoJson 7. Se nell'Arduino IDE hai attivato gli avvisi del compilatore vedrai righe come "'DynamicJsonDocument' is deprecated: use JsonDocument instead"; sono avvisi, non errori, e il programma funziona normalmente. Se hai difficoltà a installare le librerie, consulta la guida Come aggiungere una libreria Arduino.
Materiale e schema dei collegamenti
Per il progetto completo ti servono una scheda di sviluppo ESP32, un sensore di movimento PIR HC-SR501, un modulo sensore di gas MQ-2, un modulo relè da 5 V, una resistenza da 10 kΩ e una da 20 kΩ, una breadboard e dei cavetti jumper. Come LED useremo il LED blu integrato dell'ESP32 sul GPIO 2. Per provare i primi due passi bastano l'ESP32 e un cavo USB; i sensori puoi aggiungerli dopo.




| Collegamento | 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 (tramite partitore 10 kΩ / 20 kΩ) | GPIO 34 | A0 | GP26 (ADC0) |
| Relè VCC | VIN (5V) | VIN (5V) | VBUS (5V) |
| Relè IN | GPIO 26 | D1 (GPIO 5) | GP15 |
| LED | GPIO 2 (integrato) | D4 (integrato, si accende con LOW) | "LED" (integrato) |
| Tutti i GND | GND | GND | GND |
Fai attenzione anche all'alimentazione: da solo il riscaldatore dell'MQ-2 assorbe circa 150 mA e la bobina del relè intorno ai 70 mA, mentre l'ESP32 può arrivare per un attimo fino a 250 mA quando trasmette in Wi-Fi. La porta USB del computer non sempre regge questo totale e la scheda può riavviarsi proprio mentre invia un messaggio. Usa un alimentatore da 5 V di almeno 1 A e un buon cavo; se il problema continua, leggi ESP32 Brownout e Guru Meditation. Puoi vedere tutti i pin della tua scheda nello strumento Pinout.
Primo test: inviare un messaggio dall'ESP32 a Telegram
Prima di passare ai sensori, verifichiamo token e chat ID con il codice più semplice possibile. Nel codice qui sotto sostituisci nome della rete Wi-Fi, password e le righe BOT_TOKEN e CHAT_ID con i tuoi dati e caricalo sulla scheda. Poi apri il Monitor seriale a 115200 baud:
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}Nel giro di pochi secondi il tuo bot dovrebbe mandarti "Hello! ESP32 is online" su Telegram e il Monitor seriale dovrebbe mostrare "Mesaj gonderildi / message sent". sendMessage() restituisce true quando il messaggio viene consegnato; se vedi "message failed", il problema sta nella connessione Wi-Fi, nel token, nel chat ID o nell'impostazione del certificato. Prima di dare la colpa al codice, apri api.telegram.org/botTOKEN/getMe nel browser: con un token valido vedrai "ok":true e il nome del tuo bot, con uno sbagliato "Unauthorized".
Impostazione del certificato: setCACert o setInsecure?
Telegram accetta solo HTTPS, per questo il codice usa WiFiClientSecure invece di un semplice WiFiClient. In una connessione cifrata l'ESP32 deve essere sicuro che il server con cui parla sia davvero api.telegram.org; lo fa verificando il certificato del server con il certificato radice in cima alla catena. UniversalTelegramBot include questo certificato radice già pronto con il nome TELEGRAM_CERTIFICATE_ROOT. La catena di certificati di Telegram arriva a Go Daddy Root Certificate Authority - G2, valido fino alla fine del 2037; se un giorno Telegram cambiasse fornitore di certificati, andrebbe aggiornata anche la libreria.
| Impostazione | Cosa fa | Sicurezza | Quando usarla? |
|---|---|---|---|
| client.setCACert(TELEGRAM_CERTIFICATE_ROOT) | Cifra la connessione e verifica l'identità del server | Alta | Sempre, è l'impostazione consigliata |
| client.setInsecure() | Cifra la connessione ma non controlla chi è il server | Media: non si accorge di un server falso nel mezzo | Test veloce per escludere problemi di certificato |
| Nessuna delle due | Con i core ESP32 attuali la connessione non viene mai stabilita | Non funziona | Mai: non parte nessun messaggio e nel Monitor seriale non compare alcun errore |
| ESP8266: setTrustAnchors(&cert) | L'equivalente su ESP8266; il certificato si passa come X509List | Alta | NodeMCU e Wemos D1 Mini; serve l'ora corretta |
Controllare LED e relè con i comandi Telegram (/ac, /kapat)
Ora rendiamo il bot bidirezionale. Il codice qui sotto chiede a Telegram una volta al secondo se ci sono messaggi nuovi; se il testo è /ac ("accendi" in turco) accende relè e LED, /kapat ("spegni") li spegne, /durum ("stato") comunica lo stato attuale e /start invia l'elenco dei comandi. Puoi rinominarli in /accendi e /spegni, ma ricorda che i comandi di Telegram possono contenere solo lettere latine minuscole, numeri e trattini bassi, al massimo 32 caratteri. Un comando con lettere accentate come "/più" non viene riconosciuto come comando toccabile.
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}| Riga | Funzione |
|---|---|
| bot.getUpdates(bot.last_message_received + 1) | Chiede i messaggi successivi all'ultimo elaborato e restituisce quanti ne sono arrivati; il +1 evita che torni lo stesso messaggio |
| bot.messages[i].chat_id | L'ID della chat da cui arriva il messaggio; la risposta va lì e il controllo di sicurezza lo usa |
| bot.messages[i].text | Il testo ricevuto; con questo confrontiamo il comando |
| metin.indexOf('@') | Taglia la parte @nome_bot che Telegram aggiunge ai comandi nei gruppi |
| ROLE_AKTIF_LOW | La maggior parte dei moduli relè si attiva con LOW; se il tuo si attiva con HIGH, mettilo a false |
| KONTROL_ARALIGI | Ogni quanto si interroga Telegram; 1000 ms risponde in fretta, 3000–5000 ms affatica meno la rete |
Sicurezza: il bot deve rispondere solo al tuo ID
Su Telegram i nomi utente dei bot sono pubblici e si possono cercare. Se qualcuno indovina allarme_casa_bot o lo vede in uno screenshot e gli scrive /ac, un ESP32 senza controlli eseguirà anche quel comando. Per questo la riga più importante del codice è il controllo chatId != CHAT_ID. Il tuo chat ID non deve per forza restare segreto: la protezione nasce dal fatto che è Telegram stesso a certificare chi ha inviato ogni messaggio, e nessuno può falsificare l'ID di un altro. Non rispondere affatto agli sconosciuti è anche una scelta voluta: finché il bot tace, nessuno capisce nemmeno che è in funzione.
- Usa una lista bianca: tieni gli ID autorizzati in un array, ignora tutti gli altri messaggi e aggiungi i familiari uno alla volta.
- Nei gruppi controlla la persona: in un gruppo il chat ID è quello del gruppo; se solo alcune persone devono poter dare comandi, confronta from_id invece di chat_id.
- Tieni il token in un file a parte: metti token e password del Wi-Fi in un file come secrets.h e lascialo fuori quando condividi il codice.
- Salta i comandi vecchi: un /ac inviato durante un blackout non deve scattare in un momento inaspettato quando la scheda si riaccende; la pulizia all'avvio del codice completo se ne occupa.
- Stato iniziale sicuro: parti sempre con il relè spento a ogni accensione; con carichi come stufette o pompe è fondamentale.
- Non affidare le cose critiche solo al bot: per una serratura o una caldaia pensa a cosa può causare un comando sbagliato e aggiungi un messaggio di conferma o un limite di tempo.
Allarme movimento e gas: progetto completo con notifiche Telegram
Ora mettiamo insieme tutti i pezzi. Il codice qui sotto ti invia una notifica Telegram con l'orario quando il sensore PIR rileva un movimento e quando la lettura del gas dell'MQ-2 supera la soglia. Nello stesso tempo ascolta i comandi /ac, /kapat, /alarm_ac, /alarm_kapat e /durum, mostra un menu di pulsanti in fondo allo schermo quando scrivi /start e aggiunge i comandi al pulsante Menu di Telegram. Per i dettagli dei sensori, guarda le guide al sensore di movimento PIR e al sensore di gas 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}| Impostazione | Valore | Spiegazione |
|---|---|---|
| GAZ_ESIK | 1800 | Valore ADC grezzo da 0 a 4095; fissalo in base alla tua misura in aria pulita |
| GAZ_FARK | 200 | La lettura deve scendere di tanto sotto la soglia prima del messaggio "tornato normale"; evita avvisi che vanno e vengono al limite |
| HAREKET_BEKLEME | 60000 ms | Lascia almeno 60 secondi tra due avvisi di movimento |
| GAZ_ISINMA | 120000 ms | L'MQ-2 dà valori alti mentre si scalda; il gas non viene controllato nei primi 2 minuti |
| KONTROL_ARALIGI | 1000 ms | Ogni quanto si chiedono a Telegram i nuovi messaggi |
| bot.maxMessageLength | 4000 | La risposta più lunga che la libreria legge; il valore predefinito è 1500 |
I due allarmi del codice funzionano in modo diverso di proposito. Quello di movimento è protetto da un tempo di attesa: un sensore PIR scatta di continuo mentre qualcuno gira per la stanza, e senza attesa il telefono si riempirebbe di decine di messaggi al minuto. Poiché il contatore parte da zero, il primo avviso arriva non prima di 60 secondi dall'accensione della scheda, che coincide anche con il minuto circa di cui un sensore PIR ha bisogno per stabilizzarsi all'avvio. Quello del gas usa invece l'isteresi: avvisa una volta quando la lettura supera la soglia, dice "tornato normale" solo quando scende 200 unità sotto e, per sicurezza, non si può silenziare con /alarm_kapat. Neanche Telegram accetta messaggi troppo ravvicinati: se provi a mandare più di un messaggio al secondo nella stessa chat o più di 20 al minuto in un gruppo, ricevi l'errore "Too Many Requests: retry after".
Bot Telegram con ESP8266 (NodeMCU)
La stessa libreria funziona anche sulle schede ESP8266 NodeMCU e Wemos D1 Mini; la logica di comandi e allarmi resta identica e cambia solo la parte della connessione. Si include ESP8266WiFi.h al posto di WiFi.h, il certificato radice si passa come X509List a setTrustAnchors() e, dato che l'ESP8266 controlla le date di validità del certificato, bisogna ottenere l'ora via NTP prima di collegarsi. Senza l'ora ogni connessione fallisce in silenzio. Il LED integrato del NodeMCU è sul pin D4 e si accende con LOW, e c'è un solo ingresso analogico, 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}Bot Telegram con Raspberry Pi Pico W e MicroPython
Il Raspberry Pi Pico W non ha bisogno di librerie apposite: con il modulo requests di MicroPython puoi inviare richieste direttamente alla Bot API di Telegram. Il codice qui sotto manda un avviso quando rileva un movimento, comanda con /ac e /kapat il relè collegato a GP15 e il LED integrato della scheda, e risponde solo al tuo chat ID. Installa un MicroPython aggiornato sul Pico W con Thonny e salva il codice come main.py. Il modulo requests di MicroPython cifra la connessione ma non verifica il certificato del server, quindi il livello di sicurezza è lo stesso di setInsecure() sull'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)Idee di progetto con un bot Telegram e l'ESP32
- Allarme per porte e finestre: con un sensore reed o un sensore PIR manda un messaggio al telefono quando c'è movimento e in casa non c'è nessuno.
- Telecamera di sicurezza con foto: scatta una foto con un ESP32-CAM quando rileva un movimento e inviala su Telegram; i passaggi sono nella guida ESP32-CAM e Telegram.
- Controllo di stanza o serra: ricevi un avviso quando la temperatura misurata con un DHT11 supera un limite e chiedi il valore attuale con /durum.
- Promemoria per l'irrigazione: ricevi un avviso quando un sensore di umidità del terreno segnala secco e accendi la pompa per qualche secondo con un comando come /sula ("annaffia").
- Avviso incendio: abbina un sensore di fiamma all'MQ-2 per controllare entrambi i pericoli insieme.
- Sensore per la cassetta delle lettere a batteria: tieni l'ESP32 in deep sleep, sveglialo all'apertura dello sportello, invia un solo messaggio e rimettilo a dormire.
- Presa remota: accendi e spegni una lampada da scrivania o un ventilatore da qualsiasi parte del mondo con /ac e /kapat tramite un relè.
Errori comuni del bot Telegram con ESP32 e soluzioni
| Sintomo o messaggio | Causa | Soluzione |
|---|---|---|
| sendMessage restituisce false e nel Monitor seriale non c'è nessun errore | Manca la riga setCACert o setInsecure, il token contiene uno spazio o il Wi-Fi non è connesso | Aggiungi la riga del certificato, ricopia il token e controlla prima la connessione Wi-Fi |
| "Forbidden: bot can't initiate conversation with a user" nel test dal browser | Non hai mai premuto Avvia nel bot oppure l'hai bloccato | Apri il bot su Telegram e premi Avvia; se l'avevi bloccato, sbloccalo |
| "Bad Request: chat not found" | Chat ID sbagliato, segno meno mancante nell'ID di un gruppo o bot rimosso dal gruppo | Rileggi l'ID con getUpdates e scrivi gli ID dei gruppi con il segno meno |
| "Unauthorized" | Il token è sbagliato o è stato sostituito con /revoke | Copia il token attuale da BotFather e ricarica il codice |
| Invia messaggi ma non risponde ai comandi | Un delay() lungo in loop(), un chat ID sbagliato o il @nome_bot aggiunto al comando in un gruppo | Attendi con millis(), taglia la parte con la @ e guarda la riga "unauthorized ID" nel Monitor seriale |
| Il bot è diventato muto dopo una foto o uno sticker | La libreria legge solo i primi 1500 caratteri di una risposta; una risposta lunga non si decodifica e lo stesso messaggio torna di continuo | Aggiungi la riga bot.maxMessageLength = 4000; in setup() |
| All'avvio la scheda esegue comandi vecchi | Telegram conserva i messaggi non letti fino a 24 ore | All'avvio leggi e scarta i messaggi in attesa |
| "Conflict: terminated by other getUpdates request" | Lo stesso token gira contemporaneamente su due dispositivi o in uno script sul computer | Crea un bot separato su BotFather per ogni dispositivo |
| "Too Many Requests: retry after 30" | I messaggi vengono inviati troppo ravvicinati | Aggiungi un tempo di attesa; al massimo un messaggio al secondo nella stessa chat |
| In compilazione "ArduinoJson.h: No such file or directory" | La libreria ArduinoJson non è installata | Installa ArduinoJson dal Gestore librerie |
| La scheda si riavvia mentre invia un messaggio | L'alimentazione non regge il picco di corrente durante la trasmissione Wi-Fi (brownout) | Alimentatore da 1 A, cavo corto e di qualità e un condensatore da 100–470 µF tra 3V3 e GND |
Con un token, un chat ID e poche righe di codice, il tuo ESP32 è diventato un piccolo assistente che ti tiene aggiornato su cosa succede in casa ed esegue i tuoi comandi. Puoi ampliare lo stesso schema con sensori di temperatura, di livello dell'acqua o delle porte e mandare gli avvisi a un gruppo di famiglia. Nell'app BlueGrays trovi passo passo, con schemi e codice, il sistema di allarme di movimento con ESP32 che avvisa su Telegram con PIR, buzzer e LED, l'allarme con rilevatore di gas e fumo MQ-2 e i progetti Wi-Fi con l'ESP32.
Domande frequenti
Un bot Telegram con ESP32 è gratuito?
Sì. Creare un bot su Telegram, usare la Bot API e inviare messaggi è del tutto gratuito e non c'è un limite mensile di messaggi. Ci sono solo limiti di velocità: circa un messaggio al secondo nella stessa chat e 20 messaggi al minuto in un gruppo. Per i progetti di casa sono più che sufficienti.
Come trovo il mio chat ID di Telegram?
Il modo più semplice è premere Avvia in un bot di identificazione come @userinfobot, che ti invia il tuo numero di ID. In alternativa scrivi al tuo bot, apri api.telegram.org/botTOKEN/getUpdates nel browser e leggi il valore id nella sezione "chat". Gli ID dei gruppi iniziano con il segno meno.
Perché il mio ESP32 non invia messaggi a Telegram?
Le cause più comuni sono non aver mai premuto Avvia nel bot, un chat ID sbagliato, uno spazio nel token o la mancanza della riga setCACert o setInsecure nel codice. Aprendo gli indirizzi getMe e sendMessage nel browser puoi provare token e chat ID senza dipendere dal codice. Controlla anche che la scheda sia collegata a una rete Wi-Fi a 2,4 GHz.
Qualcun altro può controllare il mio bot Telegram?
I nomi utente dei bot sono pubblici, quindi se nel codice non metti un controllo dell'ID, sì: chiunque trovi il bot può mandargli comandi. Confrontare il chat ID di ogni messaggio con il tuo e ignorare gli altri lo impedisce, perché è Telegram a certificare l'identità del mittente. Non condividere nemmeno il token; se trapela, sostituiscilo con /revoke su BotFather.
Si può fare un bot Telegram con l'ESP8266?
Sì. La libreria UniversalTelegramBot funziona anche su schede ESP8266 come NodeMCU e Wemos D1 Mini. Il certificato radice si passa come X509List a setTrustAnchors() e prima di collegarsi bisogna ottenere l'ora via NTP. Dato che l'ESP8266 ha meno memoria, con i messaggi lunghi l'ESP32 se la cava meglio.
L'ESP32 può inviare foto su Telegram?
Sì. Con la funzione sendPhotoByBinary() della libreria puoi inviare direttamente su Telegram un'immagine scattata da un ESP32-CAM. Un ESP32 senza fotocamera può inviare allo stesso modo un'immagine salvata su una scheda SD, oppure l'indirizzo di un'immagine online con sendPhoto(). I sistemi che mandano una foto quando rilevano un movimento sono tra i progetti di sicurezza più popolari.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
LED Lampeggiante con ESP32
Insegna l'uso di base delle uscite GPIO con l'ESP32. Otteniamo un effetto lampeggiante accendendo e spegnendo un LED dal pin digitale. Versione ESP32 del classico progetto blink su Arduino UNO; i comandi di base di blink nell'ambiente Arduino sono gli stessi, ma la disposizione dei pin e i livelli GPIO a 3,3 V dell'ESP32 sono diversi da quelli di Arduino UNO.

Lampeggio del LED interno dell'ESP32
Far lampeggiare il LED blu integrato nell'ESP32 DevKit V1. Non sono necessari componenti esterni! Il primo progetto da provare quando si acquista un ESP32. Il GPIO 2 è collegato al LED interno.

Sistema di Allarme Movimento ESP32 (PIR + Buzzer + LED + Telegram)
Un sistema di allarme di sicurezza intelligente che attiva buzzer e LED quando il sensore PIR rileva movimento, inviando contemporaneamente una notifica istantanea al proprietario tramite Telegram. Dispone di un tempo di attesa di 30 secondi per limitare la frequenza degli allarmi ripetuti; questo meccanismo non verifica la validità dei rilevamenti del sensore PIR né elimina le false attivazioni. Adatto a sicurezza domestica, monitoraggio di garage, magazzini o studi. Con l'ESP32-CAM è possibile

Allarme rilevatore di gas/fumo ESP32 MQ-2
In questo progetto, il rilevamento di gas combustibili (GPL, metano, propano) e fumo viene effettuato tramite un modulo sensore di gas MQ-2; al rilevamento viene emesso un segnale acustico simile a una sirena tramite un cicalino passivo. L'uscita digitale di soglia (DO) del sensore viene collegata al GPIO 4 dell'ESP32; quando gas o fumo superano la soglia, DO passa a LOW e l'allarme si attiva. La sensibilità si regola tramite il potenziometro del modulo. Il cicalino è pilotato con tone().

Illuminazione Intelligente ESP32 (PIR + LDR + Relè + OLED)
Trasforma il tuo corridoio, bagno o dispensa in illuminazione intelligente con ESP32! Utilizzando un sensore di movimento PIR, un sensore di luce LDR e un modulo relè, costruiamo lo scenario classico "la luce si accende quando viene rilevato movimento al buio" — se l'ambiente è già luminoso, il movimento NON attiva la luce, risparmiando energia durante il giorno. La luce rimane accesa per 30 secondi; se il movimento continua, il timer si azzera e la luce rimane accesa più a lungo. Il display OLE

Illuminazione automatica con sensore di movimento PIR ESP32
In questo progetto, utilizzeremo il sensore di movimento PIR HC-SR501 per rilevare la presenza di una persona che entra nella stanza e accendere automaticamente il LED. Monitoreremo il tempo di accensione del LED con millis(); ogni lettura HIGH del sensore aggiornerà l'istante dell'ultimo movimento e il LED si spegnerà quando saranno trascorsi almeno 10 secondi dall'ultima lettura HIGH. Il codice attuale attende anche 50 ms a ogni iterazione con delay(50), quindi il ciclo dell'applicazione n

Connessione Wi-Fi STA con ESP32 (con display LCD)
In questo progetto colleghiamo l'ESP32 alla rete Wi-Fi domestica in modalità Station (STA) in modo sicuro e tollerante agli errori. Separiamo SSID e password in un file secrets.h per impedire fughe su Git, impostiamo un meccanismo di riconnessione automatica e visualizziamo il livello del segnale RSSI e l'indirizzo IP su un display LCD I2C. Il LED interno funge da indicatore di stato.
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 200 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

