#include <WiFi.h>
#include <HTTPClient.h>
#include <DHT.h>
/* ====== CONFIG ====== */
const char* WIFI_SSID = "";
const char* WIFI_PASS = "";
const String BOT_TOKEN = "";
const String CHAT_ID = "";
// Pines
const int PIN_BOTON = 38; // botón a GND con INPUT_PULLUP
const int PIN_DHT = 36; // DHT11 DATA
const int PIN_LED = 37; // LED externo
#define DHTTYPE DHT11
DHT dht(PIN_DHT, DHTTYPE);
// Umbrales (ajusta a gusto)
float T_MIN = 15.0, T_MAX = 30.0; // °C
float H_MIN = 20.0, H_MAX = 90.0; // %HR
// Timings
const unsigned long DEBOUNCE_MS = 50;
const unsigned long COOLDOWN_BTN = 3000;
const unsigned long COOLDOWN_DHT = 60000;
const unsigned long DHT_SAMPLE_MS = 5000;
/* ====== UTILS ====== */
String urlencode(const String &s) {
String o; o.reserve(s.length()*3);
for (uint16_t i=0;i<s.length();i++){
uint8_t c = (uint8_t)s[i];
if (c==' ') o+="%20";
else if (c=='\n') o+="%0A";
else if ((c>='0'&&c<='9')||(c>='A'&&c<='Z')||(c>='a'&&c<='z')||c=='-'||c=='_'||c=='.'||c=='~') o+=(char)c;
else { char buf[4]; snprintf(buf,sizeof(buf),"%%%02X",c); o+=buf; }
}
return o;
}
bool sendMessage(const String &text) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("[sendMessage] WiFi NO conectado");
return false;
}
HTTPClient http;
String url = "https://api.telegram.org/bot" + BOT_TOKEN +
"/sendMessage?chat_id=" + CHAT_ID +
"&text=" + urlencode(text);
Serial.print("[sendMessage] URL: ");
Serial.println(url);
http.begin(url);
int code = http.GET();
Serial.printf("[sendMessage] HTTP %d\n", code);
http.end();
return (code == 200);
}
/* ====== TELEGRAM getUpdates (parsing simple) ====== */
long lastUpdateId = 0;
void pollTelegram() {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
String url = "https://api.telegram.org/bot" + BOT_TOKEN + "/getUpdates?timeout=0";
if (lastUpdateId) url += "&offset=" + String(lastUpdateId + 1);
http.begin(url);
int code = http.GET();
if (code != 200) { Serial.printf("[getUpdates] HTTP %d\n", code); http.end(); return; }
String res = http.getString();
http.end();
int pos = 0;
while (true) {
int u1 = res.indexOf("\"update_id\":", pos); if (u1 < 0) break;
int u2 = res.indexOf(",", u1); if (u2 < 0) break;
lastUpdateId = res.substring(u1 + 12, u2).toInt();
int c1 = res.indexOf("\"chat\":{\"id\":", u2); if (c1 < 0) { pos = u2; continue; }
c1 += 13;
int c2 = res.indexOf(",", c1); if (c2 < 0) break;
String chatIdRx = res.substring(c1, c2); chatIdRx.trim();
int t1 = res.indexOf("\"text\":\"", c2); if (t1 < 0) { pos = c2; continue; }
t1 += 8;
int t2 = res.indexOf("\"", t1); if (t2 < 0) break;
String text = res.substring(t1, t2);
Serial.printf("[RX] chat:%s txt:%s\n", chatIdRx.c_str(), text.c_str());
// Solo acepta comandos del chat autorizado
if (chatIdRx != CHAT_ID) { pos = t2; continue; }
if (text == "/led_on") {
digitalWrite(PIN_LED, HIGH);
sendMessage("💡 LED: ON");
} else if (text == "/led_off") {
digitalWrite(PIN_LED, LOW);
sendMessage("💡 LED: OFF");
} else if (text == "/status") {
float h = dht.readHumidity();
float t = dht.readTemperature(); // °C
String s = "ℹ️ Estado:\nLED: ";
s += (digitalRead(PIN_LED) ? "ON" : "OFF");
if (isnan(h) || isnan(t)) s += "\nDHT11: sin lectura";
else s += "\nTemp: " + String(t,1) + "°C\nHum: " + String(h,0) + "%";
sendMessage(s);
} else {
sendMessage("Comandos: /led_on /led_off /status");
}
pos = t2;
}
}
/* ====== BOTÓN (antirrebote) ====== */
unsigned long lastBtnChange = 0, lastBtnAlert = 0;
int lastBtnStable = HIGH;
/* ====== DHT (umbrales) ====== */
unsigned long lastDhtSample = 0, lastDhtAlert = 0;
/* ====== SETUP / LOOP ====== */
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println("===== ARRANCANDO ESP32 TELEGRAM =====");
Serial.printf("PIN_BOTON=%d PIN_LED=%d PIN_DHT=%d\n", PIN_BOTON, PIN_LED, PIN_DHT);
pinMode(PIN_BOTON, INPUT_PULLUP);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, LOW);
dht.begin();
Serial.println("DHT inicializado");
WiFi.begin(WIFI_SSID, WIFI_PASS);
Serial.print("Conectando a WiFi");
while (WiFi.status() != WL_CONNECTED) { delay(300); Serial.print("."); }
Serial.println("\nWiFi conectado");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
bool ok = sendMessage("ESP32 listo ✅\nComandos: /led_on /led_off /status");
Serial.printf("Mensaje de arranque enviado: %s\n", ok ? "OK" : "ERROR");
}
void loop() {
unsigned long now = millis();
// DEBUG: estado crudo del botón cada 200 ms
static unsigned long lastBtnDebug = 0;
if (now - lastBtnDebug > 200) {
lastBtnDebug = now;
int raw = digitalRead(PIN_BOTON);
Serial.printf("[BTN] raw=%d stable=%d\n", raw, lastBtnStable);
}
// (1) Botón → Telegram
int val = digitalRead(PIN_BOTON);
if (val != lastBtnStable && (now - lastBtnChange) > DEBOUNCE_MS) {
lastBtnChange = now;
lastBtnStable = val;
Serial.printf("[BTN] cambio detectado, val=%d\n", val);
if (val == LOW && (now - lastBtnAlert) > COOLDOWN_BTN) {
lastBtnAlert = now;
Serial.println("[BTN] Botón presionado → enviando mensaje");
bool ok = sendMessage("🔘 Botón presionado");
Serial.printf("[BTN] sendMessage = %s\n", ok ? "OK" : "ERROR");
}
}
// (2) DHT11 → umbrales
if (now - lastDhtSample > DHT_SAMPLE_MS) {
lastDhtSample = now;
float h = dht.readHumidity();
float t = dht.readTemperature(); // °C
if (!isnan(h) && !isnan(t)) {
bool fuera = (t < T_MIN || t > T_MAX || h < H_MIN || h > H_MAX);
Serial.printf("DHT11: T=%.1fC H=%.0f%% %s\n", t, h, fuera ? "(fuera)" : "");
if (fuera && (now - lastDhtAlert) > COOLDOWN_DHT) {
lastDhtAlert = now;
String msg = "⚠️ Umbral DHT11\nTemp: " + String(t,1) + "°C (" +
String(T_MIN,0) + "-" + String(T_MAX,0) + ")\nHum: " +
String(h,0) + "% (" + String(H_MIN,0) + "-" + String(H_MAX,0) + ")";
bool ok = sendMessage(msg);
Serial.printf("[DHT] sendMessage = %s\n", ok ? "OK" : "ERROR");
}
} else {
Serial.println("DHT11: lectura fallida");
}
}
// (3) Telegram → control LED
static unsigned long lastPoll = 0;
if (now - lastPoll > 1500) { lastPoll = now; pollTelegram(); }
}