Community project
ESP32 Telegram Motion Alarm

This project turns an ESP32 into a motion-activated alarm system that sends instant alerts via Telegram. When the HC-SR501 PIR sensor detects movement, the device triggers a buzzer and posts a notification to a Telegram chat, making it ideal for security monitoring, garage alerts, or motion-triggered notifications in any space.
The guide includes a complete wiring diagram showing how to connect the PIR sensor and buzzer to the ESP32, a full parts list, step-by-step assembly instructions, and ready-to-use firmware. After configuration with WiFi credentials and a Telegram bot token, the system is ready to monitor and alert.
Wiring diagram
Interactive · read-only
Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Parts list
Bill of materials| Component | Qty | Notes |
|---|---|---|
| HC-SR501 PIR Motion SensorHC-SR501 | 1 | Passive Infrared (PIR) motion detection module with adjustable sensitivity and delay potentiometers. Operates on 5V supply; digital output is nominally 3.3V or 5V depending on module variant (verify before connecting directly to ESP32 3.3V GPIO — use a voltage divider if output is 5V). Outputs HIGH when motion is detected, LOW when idle. Ideal for triggering countdown timer resets in Focus Mode applications. No firmware library required — output read via standard GPIO digitalRead(). |
| Buzzer3.3 V active buzzer module | 1 | Piezo buzzer for sound output |
Assembly
5 stepsKeep the ESP32 disconnected while wiring
Unplug the ESP32 USB cable before making or moving any connections.
- Tip: Use breadboard jumper wires and check each pin label before powering the circuit.
- ⚠ Do not connect a 5 V wire to an ESP32 GPIO pin.
Connect the PIR sensor power
Connect pir_1 VCC to the ESP32 VIN/5V pin and pir_1 GND to an ESP32 GND pin.
- Tip: The HC-SR501 module accepts the board's 5 V VIN supply.
- ⚠ VCC and GND reversed can damage the PIR module.
Connect the PIR signal directly
Connect pir_1 OUT directly to ESP32 GPIO27. No resistors are required for a normal HC-SR501 PIR module because its OUT pin uses approximately 3.3 V logic.
- Tip: Use the OUT pin, not the nearby sensitivity or delay adjustment controls.
- ⚠ This direct connection applies to an HC-SR501 module. Do not directly connect an unknown 5 V-output sensor to GPIO27.
Connect the active buzzer
Connect buzzer_1 SIGNAL to ESP32 GPIO25 and buzzer_1 GND to ESP32 GND. This project assumes the specified 3.3 V active buzzer module.
- Tip: A shared GND connection is essential for the PIR and buzzer to work correctly.
- ⚠ If your buzzer is marked 5 V only, do not power it from ESP32 3V3 or attach it directly without a transistor driver.
Power and test
Recheck all connections, then connect the ESP32 through USB. After deploying the firmware, leave the PIR still for 30–60 seconds while it stabilizes before testing motion.
- Tip: Set the PIR delay control low at first so it resets quickly between tests.
- Tip: Use Schematik's Deploy button after adding your Wi-Fi and Telegram credentials in the firmware.
- ⚠ The project is USB powered; do not power the ESP32 from USB and a separate VIN supply at the same time.
Pin assignments
Board wiring reference| Pin | Connection | Type |
|---|---|---|
| VIN | pir_1 VCC | power |
| GND | pir_1 GND | ground |
| GPIO 27 | pir_1 OUT | digital |
| GND | buzzer_1 GND | ground |
| GPIO 25 | buzzer_1 SIGNAL | digital |
Firmware
ESP32#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
// Enter the Wi-Fi network the ESP32 will use.
// Forward declarations
String urlEncode(const String &text);
bool telegramIsConfigured();
bool sendTelegramMessage(const String &message);
void connectWiFi();
void startAlarm();
void updateBuzzer();
const char *WIFI_SSID = "YOUR_WIFI_NAME";
const char *WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
// Create a bot with @BotFather in Telegram, then enter its token and your chat ID.
const char *TELEGRAM_BOT_TOKEN = "YOUR_BOT_TOKEN";
const char *TELEGRAM_CHAT_ID = "YOUR_CHAT_ID";
constexpr int PIR_PIN = 27;
constexpr int BUZZER_PIN = 25;
constexpr unsigned long ALERT_COOLDOWN_MS = 60000UL;
constexpr unsigned long BUZZ_DURATION_MS = 3000UL;
bool previousMotion = false;
bool buzzerOn = false;
unsigned long lastAlertMs = 0;
unsigned long buzzerStartedMs = 0;
String urlEncode(const String &text) {
const char *hex = "0123456789ABCDEF";
String encoded;
encoded.reserve(text.length() * 3);
for (size_t i = 0; i < text.length(); ++i) {
const uint8_t c = static_cast<uint8_t>(text[i]);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~') {
encoded += static_cast<char>(c);
} else {
encoded += '%';
encoded += hex[c >> 4];
encoded += hex[c & 0x0F];
}
}
return encoded;
}
bool telegramIsConfigured() {
return String(TELEGRAM_BOT_TOKEN) != "YOUR_BOT_TOKEN" &&
String(TELEGRAM_CHAT_ID) != "YOUR_CHAT_ID";
}
bool sendTelegramMessage(const String &message) {
if (WiFi.status() != WL_CONNECTED || !telegramIsConfigured()) return false;
WiFiClientSecure client;
client.setInsecure();
HTTPClient https;
const String url = String("https://api.telegram.org/bot") + TELEGRAM_BOT_TOKEN +
"/sendMessage?chat_id=" + urlEncode(TELEGRAM_CHAT_ID) +
"&text=" + urlEncode(message);
if (!https.begin(client, url)) return false;
const int httpCode = https.GET();
https.end();
return httpCode == HTTP_CODE_OK;
}
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
const unsigned long startMs = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startMs < 20000UL) {
delay(250);
Serial.print('.');
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
Serial.print("Wi-Fi connected. IP: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("Wi-Fi not connected; local alarm works and Telegram will retry on the next event.");
}
}
void startAlarm() {
digitalWrite(BUZZER_PIN, HIGH);
buzzerOn = true;
buzzerStartedMs = millis();
}
void updateBuzzer() {
if (buzzerOn && millis() - buzzerStartedMs >= BUZZ_DURATION_MS) {
digitalWrite(BUZZER_PIN, LOW);
buzzerOn = false;
}
}
void setup() {
Serial.begin(115200);
pinMode(PIR_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
connectWiFi();
Serial.println("Motion alarm ready. Let the PIR settle for 30-60 seconds after power-up.");
}
void loop() {
updateBuzzer();
const bool motionNow = digitalRead(PIR_PIN) == HIGH;
const unsigned long now = millis();
if (motionNow && !previousMotion && now - lastAlertMs >= ALERT_COOLDOWN_MS) {
lastAlertMs = now;
startAlarm();
Serial.println("Motion detected: alarm started.");
if (sendTelegramMessage("Motion detected by your ESP32 security alarm.")) {
Serial.println("Telegram alert sent.");
} else {
Serial.println("Telegram alert not sent. Check Wi-Fi, token, chat ID, and start the bot chat.");
}
}
previousMotion = motionNow;
static unsigned long lastReconnectMs = 0;
if (WiFi.status() != WL_CONNECTED && now - lastReconnectMs >= 30000UL) {
lastReconnectMs = now;
WiFi.disconnect();
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
delay(20);
}“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.
Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.