1. Telegram Bot untuk IoT
Telegram Bot adalah program otonom yang berjalan di platform Telegram dan bisa berinteraksi dengan pengguna melalui pesan teks, tombol inline, dan media. Dalam konteks IoT, Telegram Bot menjadi salah satu antarmuka paling powerful dan gratis untuk mengontrol serta memantau perangkat dari jarak jauh.
Bayangkan Anda sedang bepergian dan ingin mengetahui suhu di gudang penyimpanan. Cukup buka Telegram, ketik /suhu, dan dalam hitungan detik bot akan membalas dengan data suhu real-time dari sensor yang terhubung ke ESP32 di rumah Anda. Tidak perlu aplikasi khusus, tidak perlu server mahal â hanya Telegram yang sudah terinstall di hampir semua smartphone.
Mengapa Telegram Bot untuk IoT?
| Kelebihan | Penjelasan |
|---|---|
| Gratis sepenuhnya | Telegram Bot API tersedia gratis tanpa batasan pesan |
| Cross-platform | Bisa diakses dari Android, iOS, desktop, dan web |
| Tidak perlu server | Tidak perlu hosting VPS atau cloud untuk relay pesan | Rich media | Mendukung teks, foto, dokumen, lokasi, dan tombol inline |
| Keamanan end-to-end | Telegram menyediakan enkripsi untuk privasi pengguna |
| Mudah dikembangkan | API sederhana, banyak library Arduino/Python yang tersedia |
| Komunitas besar | Ekosistem Telegram yang luas memudahkan integrasi |
Use Cases Telegram Bot di IoT
- Monitoring Suhu & Kelembaban â Kirim notifikasi otomatis saat suhu melebihi batas tertentu dari sensor DHT11/DHT22
- Kontrol Relay â Nyalakan/matikan lampu, pompa, atau perangkat rumah pintar lewat perintah teks
- Sistem Keamanan â Kirim foto dari ESP32-CAM saat terdeteksi gerakan
- Status Sistem â Laporkan status baterai, koneksi WiFi, dan uptime perangkat secara periodik
- Data Logging â Catat data sensor ke spreadsheet Google Sheets atau database InfluxDB
- Kontrol Servo/Stepper â Gerakkan servo atau motor stepper dari jarak jauh untuk aplikasi robotika
ââââââââââââââââ WiFi ââââââââââââââââ
â â ââââââââââââââââââââē â â
â Telegram â Internet â Telegram â
â Cloud â â Bot API â
â Server â â Server â
â â ââââââââââââââââââââē â â
ââââââââââââââââ ââââââââŦââââââââ
â HTTP Request
â (Webhook/Polling)
ââââââ´âââââ
â ESP32 â
â â
â GPIO + â
â Sensor â
âââââââââââ
Alur Komunikasi:
1. User mengirim pesan ke Bot di Telegram
2. Telegram Server meneruskan ke Bot API
3. ESP32 mengambil pesan via HTTP polling/Webhook
4. ESP32 memproses dan mengirim balasan
5. Bot mengirim respons ke user di Telegram
Telegram Bot API mendukung dua metode komunikasi: Polling (ESP32 secara aktif mengecek pesan baru) dan Webhook (Telegram mengirim pesan ke URL server). Untuk proyek pemula, polling lebih sederhana karena tidak memerlukan IP publik atau domain SSL.
2. Membuat Bot di Telegram (BotFather)
Sebelum menghubungkan ESP32 dengan Telegram, kita perlu membuat Bot terlebih dahulu. Telegram menyediakan tool bernama BotFather â sebuah bot resmi dari Telegram yang membantu membuat dan mengatur bot lain.
Langkah 1: Buka BotFather
- Buka aplikasi Telegram di smartphone atau desktop
- Cari @BotFather (pastikan ada centang biru verified)
- Mulai percakapan dengan mengklik tombol Start atau /start
Langkah 2: Buat Bot Baru
Ketik perintah berikut di chat BotFather:
/newbot
BotFather akan meminta dua informasi:
- Nama Bot â Nama tampilan yang bisa menggunakan spasi (contoh:
IoT Monitor Bot) - Username Bot â Username unik yang diakhiri dengan
bot(contoh:iot_monitor_xxx_bot)
Langkah 3: Simpan Bot Token
Bot Token adalah kunci akses ke bot Anda. Siapa pun yang memiliki token ini bisa mengontrol bot sepenuhnya. Simpan token di tempat aman dan jangan pernah membagikannya di GitHub, forum, atau file publik lainnya.
Langkah 4: Atur Deskripsi Bot (Opsional)
/setdescription IoT Monitor Bot â Pantau sensor dan kontrol perangkat ESP32 dari jarak jauh. /setabouttext Bot monitoring IoT untuk proyek smart home. /setuserpic (kirim foto profil untuk bot) /setcommands start - Mulai bot dan lihat panduan suhu - Baca suhu dari sensor DHT kelembaban - Baca kelembaban dari sensor DHT ledon - Nyalakan LED indicator ledoff - Matikan LED indicator status - Status sistem ESP32 foto - Ambil foto dari ESP32-CAM help - Tampilkan bantuan
Setelah menyetel command, Telegram akan menampilkan menu saran saat pengguna mengetik / di chat bot Anda. Ini membuat bot terlihat lebih profesional dan mudah digunakan.
Langkah 5: Dapatkan Chat ID
Untuk mengirim pesan dari ESP32 ke Anda, kita memerlukan Chat ID. Chat ID adalah identifier unik untuk setiap percakapan di Telegram. Berikut cara mendapatkannya:
- Mulai percakapan dengan bot baru Anda (klik Start)
- Kirim pesan apa pun ke bot (misal:
/start) - Buka browser, akses URL berikut (ganti
TOKENdengan token bot Anda):
https://api.telegram.org/botTOKEN/getUpdates
Cari field "chat": {"id": 123456789} di dalam respons JSON. Angka tersebut adalah Chat ID Anda. Untuk grup, format Chat ID biasanya diawali dengan tanda minus (misal: -1001234567890).
{
"ok": true,
"result": [
{
"update_id": 123456789,
"message": {
"message_id": 1,
"from": {
"id": 123456789,
"first_name": "Ahmad",
"username": "ahmad_iot"
},
"chat": {
"id": 123456789,
"type": "private"
},
"date": 1687200000,
"text": "/start"
}
}
]
}
3. ESP32 + Telegram Bot: Kirim Notifikasi
Sekarang kita akan menghubungkan ESP32 dengan Telegram Bot untuk mengirim notifikasi. Kita menggunakan library UniversalTelegramBot yang menyederhanakan proses komunikasi HTTP dengan Telegram API.
Instalasi Library
- Buka Arduino IDE â Sketch â Include Library â Manage Libraries
- Cari "Universal Telegram Bot" oleh Brian Lough
- Klik Install (dependencies seperti ArduinoJson akan ikut terinstal)
- Juga pastikan library "WiFiClientSecure" sudah ada (bawaan ESP32 Arduino core)
Kode: Kirim Notifikasi Ke ESP32
// Telegram Bot - Kirim Notifikasi dari ESP32
// IoTHub - https://iothub.id
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
// === KONFIGURASI ===
#define WIFI_SSID "NamaWiFiKamu"
#define WIFI_PASSWORD "PasswordWiFiKamu"
// Token dari BotFather (jangan bagikan!)
#define BOT_TOKEN "6123456789:AAHk2x8rGz5bY2pMvN1wLdQ3cT7fJ9uKsR4"
// Chat ID (dari getUpdates)
#define CHAT_ID "123456789"
// Interval pengiriman pesan (ms)
#define NOTIF_INTERVAL 30000
WiFiClientSecure securedClient;
UniversalTelegramBot bot(BOT_TOKEN, securedClient);
unsigned long lastNotifTime = 0;
int notifCount = 0;
void setup() {
Serial.begin(115200);
Serial.println("=== Telegram Bot IoT ===");
// Koneksi WiFi
Serial.print("Menghubungkan ke WiFi ");
Serial.print(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("WiFi terhubung! IP: ");
Serial.println(WiFi.localIP());
// Set SSL client
securedClient.setCACert(TELEGRAM_CERTIFICATE_ROOT);
// Kirim pesan startup ke Telegram
String pesan = "đ¤ *IoT Bot Online*\n";
pesan += "âââââââââââââââââ\n";
pesan += "đ IP: " + WiFi.localIP().toString() + "\n";
pesan += "đļ WiFi: " + String(WIFI_SSID) + "\n";
pesan += "â° Uptime: baru saja\n";
pesan += "âââââââââââââââââ\n";
pesan += "Ketik /help untuk bantuan";
if (bot.sendMessage(CHAT_ID, pesan, "Markdown")) {
Serial.println("Notifikasi startup terkirim!");
} else {
Serial.println("Gagal mengirim notifikasi startup.");
}
}
void loop() {
unsigned long sekarang = millis();
// Kirim notifikasi periodik setiap 30 detik
if (sekarang - lastNotifTime > NOTIF_INTERVAL) {
lastNotifTime = sekarang;
notifCount++;
// Contoh: baca suhu dummy (ganti dengan sensor asli)
float suhu = 25.0 + random(-50, 80) / 10.0;
String pesan = "đ *Status Sensor*\n";
pesan += "âââââââââââââââââ\n";
pesan += "đĄī¸ Suhu: " + String(suhu, 1) + " °C\n";
pesan += "đĻ Notif #" + String(notifCount) + "\n";
pesan += "đļ WiFi: " + String(WiFi.RSSI()) + " dBm\n";
if (suhu > 30.0) {
pesan += "â ī¸ *Peringatan: Suhu tinggi!*";
} else {
pesan += "â
Suhu normal.";
}
bot.sendMessage(CHAT_ID, pesan, "Markdown");
Serial.println("Notifikasi dikirim: " + String(notifCount));
}
// Cek apakah ada pesan masuk dari Telegram
int jumlahPesan = bot.getUpdates(bot.last_message_received + 1);
while (jumlahPesan) {
for (int i = 0; i < jumlahPesan; i++) {
String chat_id = String(bot.messages[i].chat_id);
String text = bot.messages[i].text;
Serial.println("Pesan diterima: " + text);
if (chat_id != CHAT_ID) {
bot.sendMessage(chat_id, "â Akses ditolak!", "");
continue;
}
if (text == "/start") {
String balas = "đ Selamat datang di IoT Bot!\n\n";
balas += "Perintah tersedia:\n";
balas += "/suhu â Baca suhu sensor\n";
balas += "/status â Status sistem\n";
balas += "/help â Bantuan";
bot.sendMessage(chat_id, balas, "");
}
else if (text == "/status") {
String balas = "đ *Status ESP32*\n\n";
balas += "Uptime: " + String(millis() / 1000) + " detik\n";
balas += "Free RAM: " + String(ESP.getFreeHeap()) + " byte\n";
balas += "WiFi RSSI: " + String(WiFi.RSSI()) + " dBm";
bot.sendMessage(chat_id, balas, "Markdown");
}
else if (text == "/help") {
String balas = "đ *Bantuan*\n\n";
balas += "Ketik perintah berikut:\n";
balas += "/suhu â Baca suhu\n";
balas += "/status â Info sistem\n";
balas += "/help â Tampilkan ini";
bot.sendMessage(chat_id, balas, "Markdown");
}
else {
bot.sendMessage(chat_id, "â Perintah tidak dikenal. Ketik /help", "");
}
}
jumlahPesan = bot.getUpdates(bot.last_message_received + 1);
}
delay(1000); // Delay polling
}
Kode di atas menggunakan polling, di mana ESP32 secara aktif mengecek pesan baru setiap 1 detik melalui HTTP request. Untuk proyek produksi dengan latency rendah, pertimbangkan menggunakan webhook yang memerlukan ESP32 terpublikasi di internet (via reverse proxy atau layanan seperti ngrok).
Output Serial Monitor
4. Kontrol Perangkat via Telegram
Salah satu kegunaan paling powerful dari Telegram Bot di IoT adalah kemampuan mengontrol perangkat dari jarak jauh. Dalam bagian ini, kita akan belajar mengontrol LED, relay untuk perangkat AC, dan servo motor melalui pesan Telegram.
4.1 Kontrol LED via Telegram
// Kontrol LED via Telegram Bot
// IoTHub - https://iothub.id
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
#define WIFI_SSID "NamaWiFiKamu"
#define WIFI_PASSWORD "PasswordWiFiKamu"
#define BOT_TOKEN "TOKEN_DARI_BOTFATHER"
#define CHAT_ID "CHAT_ID_ANDA"
// Pin LED
#define LED_PIN_R 25 // Merah
#define LED_PIN_G 26 // Hijau
#define LED_PIN_B 27 // Biru
WiFiClientSecure securedClient;
UniversalTelegramBot bot(BOT_TOKEN, securedClient);
unsigned long lastCheck = 0;
const int checkInterval = 1000;
void setup() {
Serial.begin(115200);
// Setup pin LED
pinMode(LED_PIN_R, OUTPUT);
pinMode(LED_PIN_G, OUTPUT);
pinMode(LED_PIN_B, OUTPUT);
// Matikan semua LED di awal
digitalWrite(LED_PIN_R, LOW);
digitalWrite(LED_PIN_G, LOW);
digitalWrite(LED_PIN_B, LOW);
// Koneksi WiFi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi terhubung!");
securedClient.setCACert(TELEGRAM_CERTIFICATE_ROOT);
bot.sendMessage(CHAT_ID, "đĄ Bot LED Control aktif!\n\nPerintah:\n/red â LED Merah\n/green â LED Hijau\n/blue â LED Biru\n/off â Matikan semua", "");
}
void loop() {
if (millis() - lastCheck > checkInterval) {
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
for (int i = 0; i < numNewMessages; i++) {
String chat_id = String(bot.messages[i].chat_id);
String text = bot.messages[i].text;
if (chat_id != CHAT_ID) {
bot.sendMessage(chat_id, "â Akses ditolak!", "");
continue;
}
String balasan = "";
if (text == "/red") {
digitalWrite(LED_PIN_R, HIGH);
digitalWrite(LED_PIN_G, LOW);
digitalWrite(LED_PIN_B, LOW);
balasan = "đ´ LED Merah NYALA";
}
else if (text == "/green") {
digitalWrite(LED_PIN_R, LOW);
digitalWrite(LED_PIN_G, HIGH);
digitalWrite(LED_PIN_B, LOW);
balasan = "đĸ LED Hijau NYALA";
}
else if (text == "/blue") {
digitalWrite(LED_PIN_R, LOW);
digitalWrite(LED_PIN_G, LOW);
digitalWrite(LED_PIN_B, HIGH);
balasan = "đĩ LED Biru NYALA";
}
else if (text == "/off") {
digitalWrite(LED_PIN_R, LOW);
digitalWrite(LED_PIN_G, LOW);
digitalWrite(LED_PIN_B, LOW);
balasan = "âĢ Semua LED dimatikan";
}
else {
balasan = "â Ketik /help untuk bantuan";
}
bot.sendMessage(chat_id, balasan, "");
}
lastCheck = millis();
}
}
4.2 Kontrol Relay (Perangkat AC)
Relay memungkinkan ESP32 mengontrol perangkat bertegangan tinggi seperti lampu rumah, kipas, atau pompa air. Berikut kode untuk kontrol relay modul 4-channel via Telegram:
ESP32 DevKit Relay Module (4-CH)
ââââââââââââ âââââââââââââââââââââââ
â â â IN1 IN2 IN3 IN4 â
â GPIO 16 âââââââââ⤠â â â â â
â GPIO 17 âââââââââ⤠â
â GPIO 18 âââââââââ⤠COM1 COM2 COM3 COM4 â
â GPIO 19 âââââââââ⤠â â â â â
â â â NC1 NC2 NC3 NC4 â
â 3.3V/5V âââââââââ⤠VCC â
â â â GND â
â GND âââââââââ⤠â â
ââââââââââââ âââââââââââââââââââââââ
Catatan: Relay modul aktif-LOW
GPIO HIGH = Relay OFF (NC terhubung)
GPIO LOW = Relay ON (NO terhubung)
// Definisi pin relay
#define RELAY_1 16 // Lampu Ruang Tamu
#define RELAY_2 17 // Kipas Angin
#define RELAY_3 18 // Pompa Air
#define RELAY_4 19 // Lampu Taman
// Status relay (aktif-LOW: 0 = ON, 1 = OFF)
bool relayStatus[4] = {HIGH, HIGH, HIGH, HIGH};
const char* namaRelay[4] = {
"Lampu Ruang Tamu",
"Kipas Angin",
"Pompa Air",
"Lampu Taman"
};
void setupRelay() {
pinMode(RELAY_1, OUTPUT);
pinMode(RELAY_2, OUTPUT);
pinMode(RELAY_3, OUTPUT);
pinMode(RELAY_4, OUTPUT);
// Semua relay OFF saat startup
digitalWrite(RELAY_1, HIGH);
digitalWrite(RELAY_2, HIGH);
digitalWrite(RELAY_3, HIGH);
digitalWrite(RELAY_4, HIGH);
}
// Fungsi handle perintah relay
String handleRelayCommand(String text) {
int relayNum = -1;
bool newState = false;
// /r1on â relay 1 ON
if (text == "/r1on") { relayNum = 0; newState = true; }
else if (text == "/r1off") { relayNum = 0; newState = false; }
else if (text == "/r2on") { relayNum = 1; newState = true; }
else if (text == "/r2off") { relayNum = 1; newState = false; }
else if (text == "/r3on") { relayNum = 2; newState = true; }
else if (text == "/r3off") { relayNum = 2; newState = false; }
else if (text == "/r4on") { relayNum = 3; newState = true; }
else if (text == "/r4off") { relayNum = 3; newState = false; }
else if (text == "/allon") {
for (int i = 0; i < 4; i++) {
digitalWrite(RELAY_1 + i, LOW);
relayStatus[i] = LOW;
}
return "â
Semua relay dinyalakan!";
}
else if (text == "/alloff") {
for (int i = 0; i < 4; i++) {
digitalWrite(RELAY_1 + i, HIGH);
relayStatus[i] = HIGH;
}
return "â Semua relay dimatikan!";
}
else {
return "";
}
if (relayNum >= 0) {
int pin = RELAY_1 + relayNum;
digitalWrite(pin, newState ? LOW : HIGH);
relayStatus[relayNum] = newState ? LOW : HIGH;
return String(newState ? "đĸ " : "đ´ ") +
namaRelay[relayNum] +
String(newState ? " dinyalakan" : " dimatikan");
}
return "";
}
4.3 Kontrol Servo via Telegram
Servo motor berguna untuk aplikasi seperti membuka tutup ventilasi, menggerakkan kamera, atau mengoperasikan mekanisme mekanis sederhana.
// Kontrol Servo via Telegram Bot
// IoTHub - https://iothub.id
#include <ESP32Servo.h>
#define SERVO_PIN 13
Servo servoPintu;
// Handle perintah servo
String handleServoCommand(String text) {
if (text == "/buka") {
servoPintu.write(90);
return "đĒ Pintu dibuka (90°)";
}
else if (text == "/tutup") {
servoPintu.write(0);
return "đĒ Pintu ditutup (0°)";
}
else if (text.startsWith("/posisi ")) {
String nilai = text.substring(8);
int sudut = nilai.toInt();
if (sudut >= 0 && sudut <= 180) {
servoPintu.write(sudut);
return "âī¸ Servo dipindah ke " + String(sudut) + "°";
} else {
return "â Sudut harus antara 0-180";
}
}
else if (text == "/servostatus") {
return "đ Posisi servo: " + String(servoPintu.read()) + "°";
}
return "";
}
// Di dalam setup():
// servoPintu.attach(SERVO_PIN);
// servoPintu.write(0); // Posisi awal tertutup
Untuk servo standar (SG90 atau MG996R), pastikan supply daya terpisah dari ESP32 karena servo membutuhkan arus yang cukup besar. Gunakan VCC eksternal 5V dan hubungkan GND bersama dengan ESP32.
5. Kirim Foto dari ESP32-CAM ke Telegram
ESP32-CAM adalah modul ESP32 yang dilengkapi kamera OV2640. Dengan Telegram Bot, kita bisa mengambil foto dan mengirimkannya langsung ke chat Telegram â sangat berguna untuk sistem keamanan atau monitoring visual jarak jauh.
Kebutuhan Komponen
- 1x ESP32-CAM (AI-Thinker)
- 1x FTDI USB-to-Serial converter (untuk programming)
- 1x Modul kamera OV2640 (biasanya sudah terpasang)
- Kabel jumper
Pengaturan Board ESP32-CAM
| Parameter | Pengaturan |
|---|---|
| Board | AI Thinker ESP32-CAM |
| Flash Frequency | 80 MHz |
| Partition Scheme | Huge APP (3MB No OTA / 1MB SPIFFS) |
| PSRAM | Enabled |
// Kirim Foto ESP32-CAM ke Telegram
// IoTHub - https://iothub.id
#include "esp_camera.h"
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
#define WIFI_SSID "NamaWiFiKamu"
#define WIFI_PASSWORD "PasswordWiFiKamu"
#define BOT_TOKEN "TOKEN_DARI_BOTFATHER"
#define CHAT_ID "CHAT_ID_ANDA"
// Pin kamera ESP32-CAM (AI-Thinker)
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
// Flash LED
#define FLASH_GPIO_NUM 4
WiFiClientSecure securedClient;
UniversalTelegramBot bot(BOT_TOKEN, securedClient);
void setupCamera() {
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_VGA; // 640x480
config.jpeg_quality = 12;
config.fb_count = 1;
config.fb_location = CAMERA_FB_IN_PSRAM;
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Gagal inisialisasi kamera: 0x%x\n", err);
return;
}
Serial.println("Kamera berhasil diinisialisasi!");
}
void setup() {
Serial.begin(115200);
// Setup kamera
setupCamera();
// Setup flash LED
pinMode(FLASH_GPIO_NUM, OUTPUT);
digitalWrite(FLASH_GPIO_NUM, LOW);
// Koneksi WiFi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi terhubung!");
securedClient.setCACert(TELEGRAM_CERTIFICATE_ROOT);
bot.sendMessage(CHAT_ID, "đˇ ESP32-CAM Bot aktif!\nKetik /foto untuk mengambil gambar.", "");
}
void kirimFotoTelegram(String chatId) {
// Nyalakan flash
digitalWrite(FLASH_GPIO_NUM, HIGH);
delay(200);
// Ambil foto
camera_fb_t *fb = esp_camera_fb_get();
digitalWrite(FLASH_GPIO_NUM, LOW);
if (!fb) {
bot.sendMessage(chatId, "â Gagal mengambil foto!", "");
return;
}
Serial.printf("Foto diambil: %d bytes\n", fb->len);
// Kirim foto ke Telegram
bool terkirim = bot.sendPhoto(
chatId,
fb->buf,
fb->len,
"đˇ Foto dari ESP32-CAM"
);
// Bebaskan buffer kamera
esp_camera_fb_return(fb);
if (terkirim) {
Serial.println("Foto berhasil dikirim ke Telegram!");
} else {
Serial.println("Gagal mengirim foto!");
}
}
void loop() {
int numMessages = bot.getUpdates(bot.last_message_received + 1);
for (int i = 0; i < numMessages; i++) {
String chatId = String(bot.messages[i].chat_id);
String text = bot.messages[i].text;
if (chatId != CHAT_ID) {
bot.sendMessage(chatId, "â Akses ditolak!", "");
continue;
}
if (text == "/foto" || text == "/ambil") {
bot.sendMessage(chatId, "đ¸ Mengambil foto...", "");
kirimFotoTelegram(chatId);
}
else if (text == "/status") {
String msg = "đˇ *Status ESP32-CAM*\n\n";
msg += "RAM bebas: " + String(ESP.getFreeHeap()) + " byte\n";
msg += "PSRAM: " + String(ESP.getFreePsram()) + " byte\n";
msg += "WiFi: " + String(WiFi.RSSI()) + " dBm";
bot.sendMessage(chatId, msg, "Markdown");
}
}
delay(1000);
}
ESP32-CAM memiliki RAM terbatas. Jika foto gagal dikirim, coba kurangi resolusi (FRAMESIZE_SVGA atau FRAMESIZE_CIF) atau tingkatkan nilai jpeg_quality (angka lebih tinggi = kualitas lebih rendah tapi ukuran file lebih kecil). Pastikan juga PSRAM aktif untuk menyimpan buffer gambar.
6. Grup Telegram untuk Monitoring IoT
Menggunakan bot dalam grup Telegram memungkinkan beberapa orang memantau perangkat IoT secara bersamaan. Ini ideal untuk tim maintenance, smart home dengan banyak pengguna, atau monitoring infrastruktur yang memerlukan notifikasi ke beberapa admin.
Langkah Membuat Grup Monitoring
- Buka Telegram dan buat grup baru (klik ikon compose â New Group)
- Tambahkan anggota yang ingin menerima notifikasi
- Berikan nama grup yang jelas (contoh:
đ Smart Home Monitor) - Tambahkan bot ke dalam grup (klik nama grup â Add Members â cari bot Anda)
- Beri bot hak admin agar bisa mengirim pesan
- Ketik
/startdi grup untuk mengaktifkan bot - Dapatkan Chat ID grup melalui
getUpdates
Kode: Bot Monitoring untuk Grup
// Bot Telegram Grup Monitoring IoT
// IoTHub - https://iothub.id
// Chat ID Grup (diawali tanda minus)
#define GRUP_CHAT_ID "-1001234567890"
// Chat ID Admin pribadi (untuk perintah admin)
#define ADMIN_CHAT_ID "123456789"
// Status notifikasi grup
bool notifikasiAktif = true;
bool notifikasiSuhu = true;
bool notifikasiGerakan = true;
// Kirim laporan periodik ke grup
void kirimLaporanGrup() {
if (!notifikasiAktif) return;
float suhu = 28.5; // Ganti dengan pembacaan sensor asli
float kelembaban = 75.0;
int baterai = 85; // Persen
String laporan = "đ *Laporan Status IoT*\n";
laporan += "âââââââââââââââââ\n";
laporan += "đĄī¸ Suhu: " + String(suhu, 1) + " °C\n";
laporan += "đ§ Kelembaban: " + String(kelembaban, 1) + " %\n";
laporan += "đ Baterai: " + String(baterai) + "%\n";
laporan += "đļ WiFi: " + String(WiFi.RSSI()) + " dBm\n";
laporan += "â° Uptime: " + String(millis() / 60000) + " menit\n";
laporan += "âââââââââââââââââ\n";
laporan += "_Laporan otomatis setiap 1 jam_";
bot.sendMessage(GRUP_CHAT_ID, laporan, "Markdown");
}
// Kirim notifikasi alarm ke grup DAN admin pribadi
void kirimAlarmGrup(String judul, String pesan) {
String alarm = "đ¨ *" + judul + "*\n\n" + pesan;
alarm += "\n\n_Silakan cek perangkat segera!_";
// Kirim ke grup
bot.sendMessage(GRUP_CHAT_ID, alarm, "Markdown");
// Juga kirim ke admin pribadi sebagai backup
bot.sendMessage(ADMIN_CHAT_ID, alarm, "Markdown");
}
// Handle perintah di dalam grup
String handleGrupCommand(String text) {
if (text == "/laporan") {
// Generate laporan real-time
return generateLaporanRealtime();
}
else if (text == "/matikannotif") {
notifikasiAktif = false;
return "đ Notifikasi grup dimatikan oleh admin.";
}
else if (text == "/nyalakannotif") {
notifikasiAktif = true;
return "đ Notifikasi grup diaktifkan kembali.";
}
else if (text == "/statusgrup") {
String status = "đ *Status Monitoring*\n\n";
status += "Notifikasi: " + String(notifikasiAktif ? "â
Aktif" : "â Nonaktif") + "\n";
status += "Alarm Suhu: " + String(notifikasiSuhu ? "â
" : "â") + "\n";
status += "Alarm Gerakan: " + String(notifikasiGerakan ? "â
" : "â");
return status;
}
return "";
}
String generateLaporanRealtime() {
// Baca data sensor aktual di sini
return "đ Laporan real-time: Suhu 28.5°C, Kelembaban 75%";
}
Untuk grup besar, pertimbangkan menggunakan Topic/Business Channel Telegram yang memungkinkan pesan diorganisasi berdasarkan kategori. Aktifkan fitur Slow Mode (1 jam) agar chat tidak banjir oleh notifikasi otomatis.
7. Keamanan Bot Token & Chat ID
Keamanan adalah aspek kritis dalam sistem IoT. Bot token yang bocor berarti siapa pun bisa mengontrol perangkat Anda dari jarak jauh. Chat ID yang tidak divalidasi memungkinkan penyerang mengirim perintah ke bot Anda.
Risiko Keamanan Umum
| Risiko | Dampak | Solusi |
|---|---|---|
| Token bocor di GitHub/public | Penyerang kontrol penuh bot | Gunakan variabel lingkungan, tidak hardcode |
| Tidak validasi Chat ID | Semua orang bisa perintah bot | Selalu cek Chat ID setiap pesan masuk |
| Tidak ada autentikasi user | Pengguna tidak dikenal bisa akses | Gunakan password/PIN perintah |
| Bot token dalam kode firmware | Mudah di-ekstrak dari ESP32 | Gunakan server relay terpisah |
| Tidak ada enkripsi payload | Data sensor terekspos di jaringan | Gunakan HTTPS/WSS selalu |
Praktik Keamanan Terbaik
// =============================================
// KEAMANAN BOT TELEGRAM â Best Practices
// IoTHub - https://iothub.id
// =============================================
// 1. JANGAN hardcode token di kode!
// Gunakan file konfigurasi terpisah:
// Buat file "credentials.h" dan .gitignore-nya:
// credentials.h (TIDAK di-commit ke repository)
#define BOT_TOKEN "6123456789:AAHk..."
#define CHAT_ID "123456789"
// 2. Validasi Chat ID dengan whitelist
#define MAX_AUTHORIZED_USERS 3
unsigned long authorizedUsers[MAX_AUTHORIZED_USERS] = {
123456789, // Admin utama
987654321, // Anggota keluarga 1
555666777 // Anggota keluarga 2
};
bool isAuthorized(String chatId) {
long id = chatId.toInt();
for (int i = 0; i < MAX_AUTHORIZED_USERS; i++) {
if (authorizedUsers[i] == id) {
return true;
}
}
return false;
}
// 3. Sistem PIN untuk perintah sensitif
String pinConfig = "1234"; // PIN perintah
bool pinVerified = false;
String handleSecureCommand(String text, String chatId) {
if (!isAuthorized(chatId)) {
return "â Anda tidak memiliki akses.";
}
// Perintah yang memerlukan PIN
if (text.startsWith("/unlock ")) {
String inputPin = text.substring(8);
if (inputPin == pinConfig) {
pinVerified = true;
return "đ Akses diberikan. Berlaku untuk 1 perintah.";
}
return "â PIN salah! Percobaan akan dilog.";
}
// Perintah berisiko tinggi memerlukan PIN
if (text == "/restart" || text == "/factoryreset") {
if (!pinVerified) {
return "đ Perintah ini memerlukan PIN.\nKetik /unlock PIN_ANDA";
}
pinVerified = false;
return "â
Perintah dieksekusi.";
}
return ""; // Lanjut ke handler normal
}
// 4. Rate limiting â cegah brute force
unsigned long lastCommandTime = 0;
int commandCount = 0;
bool checkRateLimit() {
unsigned long now = millis();
// Reset counter setiap 60 detik
if (now - lastCommandTime > 60000) {
commandCount = 0;
lastCommandTime = now;
return true;
}
commandCount++;
// Maks 20 perintah per menit
if (commandCount > 20) {
return false; // Rate limit terlampaui
}
return true;
}
// 5. Log semua aktivitas ke Serial
void logActivity(String action, String user) {
String logEntry = "[LOG] " + String(millis() / 1000) +
"s | " + action + " | User: " + user;
Serial.println(logEntry);
}
Jika token bot Anda pernah terekspos di tempat publik (misal GitHub), segera buat bot baru melalui BotFather dan revoke token lama. Jangan mencoba "memperbaiki" keamanan setelah token bocor â token yang sudah terekspos harus dianggap compromised.
8. Proyek: Alarm Suhu + Notifikasi Telegram
Mari kita gabungkan semua konsep yang telah dipelajari menjadi proyek lengkap: Sistem Alarm Suhu yang membaca suhu dari sensor DHT11 secara real-time, mengirim notifikasi Telegram saat suhu melebihi ambang batas, dan memungkinkan kontrol relay (kipas pendingin) langsung dari Telegram.
Spesifikasi Proyek
| Komponen | Fungsi | Pin ESP32 |
|---|---|---|
| ESP32 DevKit | Microcontroller utama | â |
| Sensor DHT11 | Pembaca suhu & kelembaban | GPIO 4 |
| Relay Module (1 CH) | Kontrol kipas pendingin | GPIO 16 |
| LED Merah | Indikator alarm | GPIO 2 |
| Buzzer | Alarm audio | GPIO 17 |
ESP32 DevKit
ââââââââââââââââââââ
â â
â GPIO 4 âââââââââ¤ââââââ DHT11 DATA
â GPIO 16 ââââââââ¤ââââââ Relay IN (Kipas)
â GPIO 2 âââââââââ¤ââââââ LED Merah (+ resistor)
â GPIO 17 ââââââââ¤ââââââ Buzzer (+)
â â
â 3.3V âââââââââââ¤ââââââ DHT11 VCC, Relay VCC
â GND ââââââââââââ¤ââââââ Semua GND common
ââââââââââââââââââââ
Notifikasi Flow:
Suhu > 35°C â Alarm Telegram + Relay ON + LED + Buzzer
Suhu > 30°C â Warning Telegram saja
Suhu ⤠30°C â Status normal, Relay OFF
Kode Lengkap Proyek
// =============================================
// PROYEK: Alarm Suhu + Notifikasi Telegram
// IoTHub - https://iothub.id
// =============================================
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
#include "DHT.h"
// === KONFIGURASI ===
#define WIFI_SSID "NamaWiFiKamu"
#define WIFI_PASSWORD "PasswordWiFiKamu"
#define BOT_TOKEN "TOKEN_DARI_BOTFATHER"
#define CHAT_ID "CHAT_ID_ANDA"
// === PIN KONFIGURASI ===
#define DHT_PIN 4
#define DHT_TYPE DHT11
#define RELAY_PIN 16
#define LED_ALARM 2
#define BUZZER_PIN 17
// === THRESHOLD ===
#define SUHU_ALARM 35.0 // Suhu alarm tinggi (°C)
#define SUHU_WARNING 30.0 // Suhu warning (°C)
#define INTERVAL_CEK 5000 // Interval pembacaan (ms)
#define INTERVAL_LAPORAN 300000 // Laporan periodik (5 menit)
// === OBJEK ===
DHT dht(DHT_PIN, DHT_TYPE);
WiFiClientSecure securedClient;
UniversalTelegramBot bot(BOT_TOKEN, securedClient);
// === VARIABEL STATE ===
bool alarmAktif = false;
bool kipasOtomatis = true;
bool buzzerAktif = true;
unsigned long lastCek = 0;
unsigned long lastLaporan = 0;
float suhuTertinggi = 0;
float suhuTerendah = 100;
int alarmTriggered = 0;
bool pinVerified = false;
// === Whitelist pengguna ===
unsigned long authorizedUsers[] = {123456789};
int numAuthorized = 1;
// =============================================
// SETUP
// =============================================
void setup() {
Serial.begin(115200);
Serial.println("=== Alarm Suhu Telegram IoT ===");
// Setup pin
pinMode(RELAY_PIN, OUTPUT);
pinMode(LED_ALARM, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW); // Kipas OFF (aktif-LOW)
digitalWrite(LED_ALARM, LOW);
digitalWrite(BUZZER_PIN, LOW);
// Inisialisasi DHT
dht.begin();
delay(2000);
// Koneksi WiFi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Menghubungkan WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi OK! IP: " + WiFi.localIP().toString());
// Setup SSL
securedClient.setCACert(TELEGRAM_CERTIFICATE_ROOT);
// Kirim notifikasi startup
kirimStartup();
}
// =============================================
// LOOP UTAMA
// =============================================
void loop() {
unsigned long sekarang = millis();
// --- Pembacaan Sensor Periodik ---
if (sekarang - lastCek >= INTERVAL_CEK) {
lastCek = sekarang;
prosesSensor();
}
// --- Laporan Periodik ---
if (sekarang - lastLaporan >= INTERVAL_LAPORAN) {
lastLaporan = sekarang;
kirimLaporanPeriodik();
}
// --- Cek Pesan Telegram ---
int jumlahPesan = bot.getUpdates(bot.last_message_received + 1);
while (jumlahPesan) {
for (int i = 0; i < jumlahPesan; i++) {
prosesPesanTelegram(bot.messages[i]);
}
jumlahPesan = bot.getUpdates(bot.last_message_received + 1);
}
delay(100);
}
// =============================================
// FUNGSI SENSOR
// =============================================
void prosesSensor() {
float kelembaban = dht.readHumidity();
float suhu = dht.readTemperature();
if (isnan(kelembaban) || isnan(suhu)) {
Serial.println("[ERROR] Sensor DHT tidak merespons!");
return;
}
Serial.printf("Suhu: %.1f°C | Kelembaban: %.1f%%\n",
suhu, kelembaban);
// Update statistik
if (suhu > suhuTertinggi) suhuTertinggi = suhu;
if (suhu < suhuTerendah) suhuTerendah = suhu;
// --- Alarm Logic ---
if (suhu >= SUHU_ALARM && !alarmAktif) {
alarmAktif = true;
alarmTriggered++;
kirimAlarmTelegram(suhu, kelembaban);
aktifkanAlarm();
}
else if (suhu < SUHU_ALARM && alarmAktif) {
alarmAktif = false;
matikanAlarm();
bot.sendMessage(CHAT_ID,
"â
Suhu normal kembali: " + String(suhu, 1) + "°C",
"");
}
else if (suhu >= SUHU_WARNING && suhu < SUHU_ALARM) {
// Warning level â kirim notifikasi (rate-limited)
static unsigned long lastWarning = 0;
if (millis() - lastWarning > 120000) { // Max 1 per 2 menit
lastWarning = millis();
bot.sendMessage(CHAT_ID,
"â ī¸ Suhu meningkat: " + String(suhu, 1) + "°C\n"
"Alarm akan aktif di " + String(SUHU_ALARM, 0) + "°C", "");
}
}
// --- Kontrol Kipas Otomatis ---
if (kipasOtomatis) {
digitalWrite(RELAY_PIN, suhu >= SUHU_WARNING ? HIGH : LOW);
}
}
// =============================================
// FUNGSI TELEGRAM
// =============================================
void prosesPesanTelegram(struct Message &msg) {
String chatId = String(msg.chat_id);
String text = msg.text;
// Validasi pengguna
if (!isAuthorized(chatId)) {
bot.sendMessage(chatId, "â Akses ditolak!", "");
return;
}
Serial.println("Perintah: " + text);
// --- Perintah Umum ---
if (text == "/start" || text == "/help") {
kirimMenuBantuan(chatId);
}
else if (text == "/suhu") {
float s = dht.readTemperature();
float h = dht.readHumidity();
String pesan = "đĄī¸ *Pembacaan Sensor*\n\n";
pesan += "Suhu: " + String(s, 1) + " °C\n";
pesan += "Kelembaban: " + String(h, 1) + " %\n";
pesan += "Status: " + String(s >= SUHU_ALARM ? "đ´ ALARM" :
(s >= SUHU_WARNING ? "đĄ WARNING" : "đĸ Normal"));
bot.sendMessage(chatId, pesan, "Markdown");
}
else if (text == "/status") {
kirimStatusSistem(chatId);
}
else if (text == "/kipason") {
digitalWrite(RELAY_PIN, HIGH);
bot.sendMessage(chatId, "đ¨ Kipas dinyalakan (manual)", "");
}
else if (text == "/kipasoff") {
digitalWrite(RELAY_PIN, LOW);
bot.sendMessage(chatId, "đ¨ Kipas dimatikan (manual)", "");
}
else if (text == "/kipasauto") {
kipasOtomatis = true;
bot.sendMessage(chatId, "đ¤ Mode kipas otomatis aktif", "");
}
else if (text == "/buzzeron") {
buzzerAktif = true;
bot.sendMessage(chatId, "đ Buzzer diaktifkan", "");
}
else if (text == "/buzzeroff") {
buzzerAktif = false;
digitalWrite(BUZZER_PIN, LOW);
bot.sendMessage(chatId, "đ Buzzer dinonaktifkan", "");
}
else if (text == "/resetstat") {
suhuTertinggi = 0;
suhuTerendah = 100;
alarmTriggered = 0;
bot.sendMessage(chatId, "đ Statistik di-reset", "");
}
}
// =============================================
// FUNGSI ALARM
// =============================================
void aktifkanAlarm() {
digitalWrite(LED_ALARM, HIGH);
if (buzzerAktif) {
// Pola buzzer: 3 kali beep
for (int i = 0; i < 3; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
delay(100);
}
}
if (kipasOtomatis) {
digitalWrite(RELAY_PIN, HIGH); // Kipas ON otomatis
}
}
void matikanAlarm() {
digitalWrite(LED_ALARM, LOW);
digitalWrite(BUZZER_PIN, LOW);
if (kipasOtomatis) {
digitalWrite(RELAY_PIN, LOW); // Kipas OFF
}
}
// =============================================
// FUNGSI PESAN TELEGRAM
// =============================================
void kirimStartup() {
String pesan = "đ¤ *Alarm Suhu IoT Aktif!*\n";
pesan += "âââââââââââââââââ\n";
pesan += "đ IP: " + WiFi.localIP().toString() + "\n";
pesan += "â° Mulai: " + String(millis() / 1000) + "s\n";
pesan += "đĄī¸ Alarm: > " + String(SUHU_ALARM, 0) + "°C\n";
pesan += "â ī¸ Warning: > " + String(SUHU_WARNING, 0) + "°C\n";
pesan += "âââââââââââââââââ\n";
pesan += "Ketik /help untuk panduan";
bot.sendMessage(CHAT_ID, pesan, "Markdown");
}
void kirimAlarmTelegram(float suhu, float kelembaban) {
String pesan = "đ¨ *ALARM SUHU TINGGI!*\n";
pesan += "âââââââââââââââââ\n";
pesan += "đĄī¸ Suhu: *" + String(suhu, 1) + "°C*\n";
pesan += "đ§ Kelembaban: " + String(kelembaban, 1) + "%\n";
pesan += "đ¨ Kipas: " + String(kipasOtomatis ? "AKTIF OTOMATIS" : "Manual") + "\n";
pesan += "đ Trigger ke-" + String(alarmTriggered) + "\n";
pesan += "âââââââââââââââââ\n";
pesan += "_Ketik /kipasoff untuk matikan kipas_";
bot.sendMessage(CHAT_ID, pesan, "Markdown");
}
void kirimLaporanPeriodik() {
float suhu = dht.readTemperature();
float kelembaban = dht.readHumidity();
String pesan = "đ *Laporan Periodik*\n";
pesan += "âââââââââââââââââ\n";
pesan += "đĄī¸ Saat ini: " + String(suhu, 1) + "°C\n";
pesan += "đ§ Kelembaban: " + String(kelembaban, 1) + "%\n";
pesan += "đ Tertinggi: " + String(suhuTertinggi, 1) + "°C\n";
pesan += "đ Terendah: " + String(suhuTerendah, 1) + "°C\n";
pesan += "đ¨ Alarm: " + String(alarmTriggered) + "x\n";
pesan += "âąī¸ Uptime: " + String(millis() / 60000) + " menit\n";
pesan += "đļ WiFi: " + String(WiFi.RSSI()) + " dBm";
bot.sendMessage(CHAT_ID, pesan, "Markdown");
}
void kirimMenuBantuan(String chatId) {
String pesan = "đ *Panduan Alarm Suhu IoT*\n\n";
pesan += "*Monitoring:*\n";
pesan += "/suhu â Baca suhu saat ini\n";
pesan += "/status â Status sistem lengkap\n\n";
pesan += "*Kontrol Kipas:*\n";
pesan += "/kipason â Nyalakan kipas\n";
pesan += "/kipasoff â Matikan kipas\n";
pesan += "/kipasauto â Mode otomatis\n\n";
pesan += "*Pengaturan:*\n";
pesan += "/buzzeron â Aktifkan buzzer\n";
pesan += "/buzzeroff â Nonaktifkan buzzer\n";
pesan += "/resetstat â Reset statistik";
bot.sendMessage(chatId, pesan, "Markdown");
}
void kirimStatusSistem(String chatId) {
float suhu = dht.readTemperature();
float kelembaban = dht.readHumidity();
String pesan = "đ *Status Sistem*\n";
pesan += "âââââââââââââââââ\n";
pesan += "đĄī¸ Suhu: " + String(suhu, 1) + "°C\n";
pesan += "đ§ Kelembaban: " + String(kelembaban, 1) + "%\n";
pesan += "đ¨ Kipas: " + String(digitalRead(RELAY_PIN) ? "ON" : "OFF") + "\n";
pesan += "đ Alarm: " + String(alarmAktif ? "AKTIF" : "OFF") + "\n";
pesan += "đ Buzzer: " + String(buzzerAktif ? "ON" : "OFF") + "\n";
pesan += "đ¤ Kipas Auto: " + String(kipasOtomatis ? "YA" : "TIDAK") + "\n";
pesan += "đ Max: " + String(suhuTertinggi, 1) + "°C\n";
pesan += "đ Min: " + String(suhuTerendah, 1) + "°C\n";
pesan += "đ¨ Total Alarm: " + String(alarmTriggered) + "\n";
pesan += "đ RAM: " + String(ESP.getFreeHeap()) + " byte\n";
pesan += "đļ WiFi: " + String(WiFi.RSSI()) + " dBm";
bot.sendMessage(chatId, pesan, "Markdown");
}
bool isAuthorized(String chatId) {
long id = chatId.toInt();
for (int i = 0; i < numAuthorized; i++) {
if (authorizedUsers[i] == id) return true;
}
return false;
}
Untuk versi production, tambahkan fitur: multi-sensor (suhu ruangan + luar), data logging ke SD card, grafik suhu via Telegram inline keyboard, dan kalibrasi notifikasi via perintah (/setalarm 37). Pertimbangkan juga menggunakan deep sleep mode untuk hemat daya pada proyek baterai.
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Telegram Bot untuk IoT: