IoT & Messaging

Telegram Bot untuk IoT: Kontrol & Notifikasi dari ESP32

👑 Premium

Panduan lengkap membangun Telegram Bot untuk proyek IoT — mulai dari membuat bot di BotFather hingga mengontrol perangkat dan mengirim notifikasi sensor secara real-time

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 sepenuhnyaTelegram Bot API tersedia gratis tanpa batasan pesan
Cross-platformBisa diakses dari Android, iOS, desktop, dan web
Tidak perlu serverTidak perlu hosting VPS atau cloud untuk relay pesan
Rich mediaMendukung teks, foto, dokumen, lokasi, dan tombol inline
Keamanan end-to-endTelegram menyediakan enkripsi untuk privasi pengguna
Mudah dikembangkanAPI sederhana, banyak library Arduino/Python yang tersedia
Komunitas besarEkosistem Telegram yang luas memudahkan integrasi

Use Cases Telegram Bot di IoT

Diagram: Arsitektur Komunikasi Telegram Bot + IoT
┌──────────────┐         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
💡 Tips

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

  1. Buka aplikasi Telegram di smartphone atau desktop
  2. Cari @BotFather (pastikan ada centang biru verified)
  3. Mulai percakapan dengan mengklik tombol Start atau /start

Langkah 2: Buat Bot Baru

Ketik perintah berikut di chat BotFather:

Telegram — Perintah BotFather
/newbot

BotFather akan meminta dua informasi:

  1. Nama Bot — Nama tampilan yang bisa menggunakan spasi (contoh: IoT Monitor Bot)
  2. Username Bot — Username unik yang diakhiri dengan bot (contoh: iot_monitor_xxx_bot)
Contoh Balasan BotFather: Done! Congratulations on your new bot. You will find it at t.me/iot_monitor_xxx_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. Use this token to access the HTTP API: 6123456789:AAHk2x8rGz5bY2pMvN1wLdQ3cT7fJ9uKsR4 Keep your token secure and store it safely.

Langkah 3: Simpan Bot Token

âš ī¸ Peringatan Penting

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)

Telegram — Perintah Tambahan BotFather
/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:

  1. Mulai percakapan dengan bot baru Anda (klik Start)
  2. Kirim pesan apa pun ke bot (misal: /start)
  3. Buka browser, akses URL berikut (ganti TOKEN dengan token bot Anda):
URL — Dapatkan Chat ID
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).

JSON — Contoh Respons getUpdates
{
  "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

  1. Buka Arduino IDE → Sketch → Include Library → Manage Libraries
  2. Cari "Universal Telegram Bot" oleh Brian Lough
  3. Klik Install (dependencies seperti ArduinoJson akan ikut terinstal)
  4. Juga pastikan library "WiFiClientSecure" sudah ada (bawaan ESP32 Arduino core)

Kode: Kirim Notifikasi Ke ESP32

C++ — telegram_notif.ino
// 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
}
â„šī¸ Pola Polling vs Webhook

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

Output Serial Monitor: === Telegram Bot IoT === Menghubungkan ke WiFi MyWiFi WiFi terhubung! IP: 192.168.1.105 Notifikasi startup terkirim! Pesan diterima: /start Pesan diterima: /suhu Notifikasi dikirim: 1 Notifikasi dikirim: 2

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

C++ — kontrol_led_telegram.ino
// 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:

Diagram: Koneksi ESP32 + Relay Module
    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)
C++ — Bagian kode kontrol relay
// 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.

C++ — Kontrol Servo via Telegram
// 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
💡 Tips Penggunaan Servo

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

Pengaturan Board ESP32-CAM

Parameter Pengaturan
BoardAI Thinker ESP32-CAM
Flash Frequency80 MHz
Partition SchemeHuge APP (3MB No OTA / 1MB SPIFFS)
PSRAMEnabled
C++ — esp32cam_telegram.ino
// 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);
}
âš ī¸ Catatan Penting

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

  1. Buka Telegram dan buat grup baru (klik ikon compose → New Group)
  2. Tambahkan anggota yang ingin menerima notifikasi
  3. Berikan nama grup yang jelas (contoh: 🏠 Smart Home Monitor)
  4. Tambahkan bot ke dalam grup (klik nama grup → Add Members → cari bot Anda)
  5. Beri bot hak admin agar bisa mengirim pesan
  6. Ketik /start di grup untuk mengaktifkan bot
  7. Dapatkan Chat ID grup melalui getUpdates

Kode: Bot Monitoring untuk Grup

C++ — bot_grup_monitoring.ino
// 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%";
}
â„šī¸ Tips Grup Telegram untuk IoT

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/publicPenyerang kontrol penuh botGunakan variabel lingkungan, tidak hardcode
Tidak validasi Chat IDSemua orang bisa perintah botSelalu cek Chat ID setiap pesan masuk
Tidak ada autentikasi userPengguna tidak dikenal bisa aksesGunakan password/PIN perintah
Bot token dalam kode firmwareMudah di-ekstrak dari ESP32Gunakan server relay terpisah
Tidak ada enkripsi payloadData sensor terekspos di jaringanGunakan HTTPS/WSS selalu

Praktik Keamanan Terbaik

C++ — Contoh Keamanan Bot Token
// =============================================
// 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);
}
🔴 Penting!

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 DevKitMicrocontroller utama—
Sensor DHT11Pembaca suhu & kelembabanGPIO 4
Relay Module (1 CH)Kontrol kipas pendinginGPIO 16
LED MerahIndikator alarmGPIO 2
BuzzerAlarm audioGPIO 17
Diagram: Rangkaian Alarm Suhu Telegram
              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

C++ — alarm_suhu_telegram.ino
// =============================================
// 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;
}
💡 Pengembangan Lanjutan

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:

Pertanyaan 1: Apa yang harus dilakukan pertama kali untuk membuat Telegram Bot yang bisa dikontrol dari ESP32?

a) Instal library UniversalTelegramBot di Arduino IDE
b) Buat akun Telegram baru khusus untuk ESP32
c) Beli domain dan hosting untuk server bot
d) Buat bot melalui @BotFather dan dapatkan API token

Pertanyaan 2: Dalam metode komunikasi polling, siapa yang menginisiasi permintaan HTTP ke Telegram API?

a) Telegram server mengirim notifikasi ke ESP32
b) ESP32 secara aktif mengecek pesan baru ke Telegram API
c) User harus menekan tombol kirim di Telegram
d) BotFather yang mengirim data ke ESP32

Pertanyaan 3: Bagaimana cara mendapatkan Chat ID pengguna untuk mengirim pesan dari ESP32 ke Telegram?

a) Akses endpoint getUpdates dari Telegram Bot API
b) Chat ID otomatis dikirim ke ESP32 saat pertama kali terhubung
c) Chat ID selalu sama untuk semua pengguna Telegram
d) Chat ID bisa ditemukan di pengaturan akun Telegram

Pertanyaan 4: Mengapa penting untuk melakukan validasi Chat ID saat menerima pesan di Telegram Bot untuk IoT?

a) Agar respons bot lebih cepat
b) Agar bot tidak menggunakan terlalu banyak RAM
c) Untuk mencegah pengguna tidak dikenal mengontrol perangkat IoT
d) Karena Telegram membatasi jumlah chat tanpa validasi

Pertanyaan 5: Pada proyek alarm suhu, apa yang terjadi ketika suhu melebihi batas alarm dan mode kipas otomatis aktif?

a) ESP32 akan restart untuk mendinginkan diri
b) Bot mengirim email ke admin
c) Kipas tetap mati menunggu perintah manual
d) Relay aktif menyalakan kipas, alarm Telegram dikirim, LED dan buzzer menyala
← Sebelumnya Firewall MikroTik: NAT, Filter Rules & Security Selanjutnya → Web Server ESP32: Dashboard IoT dengan HTML & Sensor