Bot de Telegram

Foco con Telegram, apagador de 3 vías, relevador y ESP8266

Encender un foco con un Bot de Telegram es posible usando una tarjeta como la NodeMCU ESP8266 ayudándonos de un relevador y un apagador de 3 vías. El apagador de 3 vías (apagador de escalera) también funcionará para poder controlar la bombilla manualmente; así puedes tener 2 opciones: controlar una bombilla con el Bot de Telegram y al mismo tiempo como si fuera un interruptor normal.

Bot de Telegram en ESP8266 para control de bombilla usando relevador

En este post te mostraré cómo apagar y encender una bombilla usando un Bot de Telegram, la NodeMCU ESP8266 y un relevador. Voy a enseñarte el circuito de conexión y el código de la tarjeta para que se pueda conectar al wifi consultando la API de Bots de Telegram y accionando el relevador como sea necesario.

Una NodeMCU ESP8266 no puede proveer los 5v que necesita el relevador, así que vamos a usar una fuente de poder externa para alimentar a la tarjeta y para activar dicho relevador. Cuando hicimos esto con Arduino el transistor no fue necesario porque el Arduino sí puede sacar 5v. De cualquier manera, el Arduino UNO no tiene wifi así que prefiero la ESP8266.

Materiales

  • 1 bombilla
  • Cables para bombilla y para circuito
  • Relevador 5v
  • Apagador de 3 vías
  • Resistencia 1k ohm
  • Transistor BC548
  • Diodo 1N4007
  • Tarjeta NodeMCU ESP8266
  • Fuente de poder de 5v para la tarjeta (puedes usar un cargador viejo)

Circuito de conexión

Nota importante: manipular la corriente eléctrica de la bombilla puede ocasionarte daños si lo haces sin protección o sin cuidado. Sigue el tutorial bajo tu propia responsabilidad.

Vamos a ver la conexión de los componentes electrónicos. Como ya te lo dije anteriormente, este proyecto te permitirá controlar el foco con un interruptor físico y también desde cualquier parte del mundo usando un Bot de Telegram.

Bombilla con Telegram, relevador y apagador de 3 vías

Bombilla

Comencemos revisando la bombilla. Recuerda que el foco tiene su propia fuente de energía, ya que al menos en México son 120v AC y no podemos alimentarlo con los 5v o 3.3 de nuestro circuito controlador.

El neutro no se interrumpe en ningún momento. La fase es la que se interrumpe; un extremo va conectado al puerto común del relevador y el otro extremo va conectado al común del apagador de 3 vías.

En la imagen de arriba la fuente de poder superior izquierda representa la alimentación de la bombilla que normalmente será la fuente de alimentación doméstica que alimentaría normalmente a cualquier foco. El cable negro representa el neutro, y el cable rojo la fase. Fíjate que cortamos el cable de la fase y nos quedan 2 puntas; una de ellas va al común del relevador y la otra al común del apagador de escalera.

Foco con Bot de Telegram, relevador y NodeMCU ESP8266

Relevador y apagador de 3 vías

Las terminales restantes del apagador de escalera y del relevador van conectadas entre sí. Conecta la terminal 1 del interruptor de 3 vías a la terminal NC (normalmente cerrado) del relevador, y la terminal 2 del interruptor de 3 vías a la terminal NO (normalmente abierto).

Ya tenemos ocupado el común, NC y NO del relevador; falta conectar las terminales que activan la bobina. Estas terminales sí van a ir conectadas a nuestra tarjeta ESP8266.

Una terminal del relevador va directamente a GND. La otra terminal va al colector del transistor.

Transistor para controlar relevador de bombilla con Bot de Telegram y NodeMCU ESP8266

Transistor

El colector del transistor va a una terminal del relevador. La base del transistor va conectada a la resistencia de 1000 ohm que a su vez está conectada al D1 de la ESP8266. Finalmente, el emisor del transistor va al GND.

Tarjeta NodeMCU ESP8266

La tarjeta no lleva conexión USB (solo al ser programada) ya que se alimenta de una fuente de poder externa de 5v. El VCC de la fuente de poder se conecta al VIN y el GND al GND. Luego, el D1 se conecta a la base del transistor.

Nunca conectes la fuente de alimentación y el USB para programar la tarjeta al mismo tiempo. Recomiendo usar el cable USB para programar la tarjeta y, una vez lista, conectar la fuente de alimentación.

Diodo flyback

Finalmente debes conectar un diodo de retorno en paralelo con las terminales del relevador. El ánodo va en la terminal donde conectas el colector del transistor, y el cátodo va conectado a la terminal donde conectas los 5V de la fuente de alimentación.

Alternativa sin apagador de 3 vías

Si quisieras hacer este circuito usando únicamente el Bot de Telegram y el relevador solo tienes que conectar un extremo de la fase al NC del relevador y el otro extremo al Común del relevador.

Código fuente

Ahora veamos el código en C++ para conectar una bombilla a un Bot de Telegram. Ya te enseñé a encender un LED con Telegram y la ESP8266 así como a enviar la temperatura y humedad a los usuarios. Este código es una combinación de ambos artículos pues vamos a obtener actualizaciones de la API de Telegram, manejar cada actualización y después enviar un teclado al usuario para que pueda intercambiar el estado del relevador y a su vez pueda encender y apagar el foco.

Primero nos conectamos a internet, configuramos el relevador y el LED integrado pues usaremos este último como indicador de errores:

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(RELEVADOR, OUTPUT);
  apagarRelevador();
  apagarLedIntegrado();
  WiFi.mode(WIFI_STA);
  WiFi.begin(NOMBRE_RED_WIFI, CLAVE_RED_WIFI);
  while (WiFi.status() != WL_CONNECTED)
  {
    parpadearLed(1);
  }
  clienteWifi->setInsecure();
  if (!clienteHttpTeclado.begin(*clienteWifi, "https://api.telegram.org/bot" + tokenTelegram + "/sendMessage"))
  {
    parpadearLed(errorIniciandoClienteTeclado);
  }
  else
  {
    encenderLedIntegrado();
  }
}

Dentro del loop consultamos la API de Bots de Telegram para saber si alguien ha enviado un mensaje a nuestro Bot o si alguien ha tocado el botón del teclado. Hacemos una petición HTTP de tipo GET:

void loop()
{
  if ((WiFi.status() != WL_CONNECTED))
  {
    parpadearLed(errorWifiEnLoop);
    return;
  }
  String url = "https://api.telegram.org/bot" + tokenTelegram + "/getUpdates?limit=" + String(cantidadActualizacionesPorPeticion);
  if (ultimoIdDeActualizacion != 0)
  {
    url += "&offset=" + String(ultimoIdDeActualizacion + 1);
  }

  if (!clienteHttpActualizaciones.begin(*clienteWifi, url))
  {
    parpadearLed(errorIniciandoClienteActualizaciones);
    return;
  }
  clienteHttpActualizaciones.addHeader("Content-Type", "application/json", false, true);
  clienteHttpActualizaciones.setTimeout(5000);
  int httpCode = clienteHttpActualizaciones.GET();
  if (httpCode > 0)
  {
    String respuestaDelServidor = clienteHttpActualizaciones.getString();
    if (httpCode == HTTP_CODE_OK)
    {
      JsonDocument documentoJson;
      DeserializationError errorAlDecodificarJson = deserializeJson(documentoJson, respuestaDelServidor);
      if (errorAlDecodificarJson)
      {
        parpadearLed(errorDecodificandoJson);
        return;
      }
      JsonArray actualizaciones = documentoJson["result"].as<JsonArray>();
      for (JsonObject actualizacion : actualizaciones)
      {
        manejarActualizacion(actualizacion, clienteWifi);
      }
    }
  }
  clienteHttpActualizaciones.end();
  // Esperar algunos segundos antes de enviar el siguiente mensaje
  delay(milisegundosEsperaEntreActualizaciones);
}

Decodificamos lo que nos haya devuelto la API de Telegram y manejamos las actualizaciones en la siguiente función. En este caso solo estoy manejando dos actualizaciones:

  1. Cuando alguien envía un mensaje de texto
  2. Cuando alguien toca un botón del teclado

Así que lo hacemos así:

void manejarActualizacion(JsonObject actualizacion, std::unique_ptr<BearSSL::WiFiClientSecure> &clienteWifi)
{
  // https://core.telegram.org/bots/api#getting-updates
  int idActualizacion = actualizacion["update_id"];
  ultimoIdDeActualizacion = idActualizacion;
  /*
  Alguien presionó un botón
  */  if (actualizacion["callback_query"].is<JsonVariant>())
  {
    const char *dataCallback = actualizacion["callback_query"]["data"];
    const int chatId = actualizacion["callback_query"]["from"]["id"];
    if (strcmp(dataCallback, callbackDataIntercambiarEstado) == 0)
    {
      intercambiarEstado();
      responderConTeclado(clienteWifi, chatId);
    }
  }
  /*
  Alguien envió un mensaje
  */  if (actualizacion["message"].is<JsonVariant>())
  {
    if (actualizacion["message"]["text"].is<String>())
    {
      /*
      Sin importar el contenido del mensaje, siempre enviamos
      el teclado

      */      String contenido = actualizacion["message"]["text"];
      int idUsuarioRemitente = actualizacion["message"]["from"]["id"];
      responderConTeclado(clienteWifi, idUsuarioRemitente);
    }
  }
}

Solo intercambiamos el estado del relevador cuando se toca el botón; para ello es que revisamos si la actualización es de tipo callback_query. Ya sea que se haya enviado un mensaje de texto o que se haya presionado un botón, siempre respondemos con un teclado que sirve para apagar y encender el foco desde Telegram usando la función responderConTeclado:

void responderConTeclado(std::unique_ptr<BearSSL::WiFiClientSecure> &clienteWifi, int chatId)
{

  String textoDelBoton = "Encender 💡";
  if (estaEncendido)
  {
    textoDelBoton = "Apagar ⚫";
  }
  String cargaUtil = "{\"chat_id\":\"" + String(chatId) + String("\",\"text\":\"Hola. Toca el botón de abajo para intercambiar el estado del foco\",\"reply_markup\":{\"inline_keyboard\":[[{\"text\":\"" + textoDelBoton + " \",\"callback_data\":\"" + String(callbackDataIntercambiarEstado) + "\"}]]}}");
  clienteHttpTeclado.addHeader("Content-Type", "application/json", false, true);
  clienteHttpTeclado.setTimeout(5000);
  int httpCode = clienteHttpTeclado.POST(cargaUtil);
  if (httpCode != HTTP_CODE_OK)
  {
    parpadearLed(errorEnviandoTeclado);
  }
  clienteHttpTeclado.end();
}

Por cierto, aquí estoy evitando crear el objeto y codificarlo como JSON. Simplemente estoy armando el JSON ya codificado como una cadena. Eres libre de usar cualquier librería para codificarlo si quieres hacerlo más limpio.

Y el código fuente completo queda así:

/**
 * Creado por Parzibyte
 * https://parzibyte.me
 *
 */#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
#include <string.h>
// Obtenido con el @Botfather
const String tokenTelegram = "Aquí va tu token";
// Red WiFi para conectarse
const char *NOMBRE_RED_WIFI = "Nombre de la red";
const char *CLAVE_RED_WIFI = "Clave de la red";
const char *callbackDataIntercambiarEstado = "0";

const int errorEnviandoTeclado = 1;
const int errorIniciandoClienteTeclado = 2;
const int errorWifiEnLoop = 3;
const int errorIniciandoClienteActualizaciones = 4;
const int errorDecodificandoJson = 5;
std::unique_ptr<BearSSL::WiFiClientSecure> clienteWifi(new BearSSL::WiFiClientSecure);
HTTPClient clienteHttpActualizaciones;
HTTPClient clienteHttpTeclado;
int ultimoIdDeActualizacion = 0;
int cantidadActualizacionesPorPeticion = 1;
bool estaEncendido = false;
int RELEVADOR = 5; // d1 pertenece al GPIO5
int milisegundosEsperaEntreActualizaciones = 200;
void apagarRelevador();
void encenderRelevador();
void encenderLedIntegrado()
{
  // Sí, para encenderlo le mandas un LOW
  digitalWrite(LED_BUILTIN, LOW);
}

void apagarLedIntegrado()
{

  // Sí, para apagarlo le mandas un HIGH
  digitalWrite(LED_BUILTIN, HIGH);
}

void parpadearLed(int cantidad)
{
  for (int i = 0; i < cantidad; i++)
  {
    apagarLedIntegrado();
    delay(200);
    encenderLedIntegrado();
    delay(200);
  }
}
void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(RELEVADOR, OUTPUT);
  apagarRelevador();
  apagarLedIntegrado();
  WiFi.mode(WIFI_STA);
  WiFi.begin(NOMBRE_RED_WIFI, CLAVE_RED_WIFI);
  while (WiFi.status() != WL_CONNECTED)
  {
    parpadearLed(1);
  }
  clienteWifi->setInsecure();
  if (!clienteHttpTeclado.begin(*clienteWifi, "https://api.telegram.org/bot" + tokenTelegram + "/sendMessage"))
  {
    parpadearLed(errorIniciandoClienteTeclado);
  }
  else
  {
    encenderLedIntegrado();
  }
}

void encenderRelevador()
{
  estaEncendido = true;
  digitalWrite(RELEVADOR, HIGH);
}

void apagarRelevador()
{
  estaEncendido = false;
  digitalWrite(RELEVADOR, LOW);
}

void responderConTeclado(std::unique_ptr<BearSSL::WiFiClientSecure> &clienteWifi, int chatId)
{

  String textoDelBoton = "Encender 💡";
  if (estaEncendido)
  {
    textoDelBoton = "Apagar ⚫";
  }
  String cargaUtil = "{\"chat_id\":\"" + String(chatId) + String("\",\"text\":\"Hola. Toca el botón de abajo para intercambiar el estado del foco\",\"reply_markup\":{\"inline_keyboard\":[[{\"text\":\"" + textoDelBoton + " \",\"callback_data\":\"" + String(callbackDataIntercambiarEstado) + "\"}]]}}");
  clienteHttpTeclado.addHeader("Content-Type", "application/json", false, true);
  clienteHttpTeclado.setTimeout(5000);
  int httpCode = clienteHttpTeclado.POST(cargaUtil);
  if (httpCode != HTTP_CODE_OK)
  {
    parpadearLed(errorEnviandoTeclado);
  }
  clienteHttpTeclado.end();
}

void intercambiarEstado()
{

  if (estaEncendido)
  {
    apagarRelevador();
  }
  else
  {
    encenderRelevador();
  }
}

void manejarActualizacion(JsonObject actualizacion, std::unique_ptr<BearSSL::WiFiClientSecure> &clienteWifi)
{
  // https://core.telegram.org/bots/api#getting-updates
  int idActualizacion = actualizacion["update_id"];
  ultimoIdDeActualizacion = idActualizacion;
  /*
  Alguien presionó un botón
  */  if (actualizacion["callback_query"].is<JsonVariant>())
  {
    const char *dataCallback = actualizacion["callback_query"]["data"];
    const int chatId = actualizacion["callback_query"]["from"]["id"];
    if (strcmp(dataCallback, callbackDataIntercambiarEstado) == 0)
    {
      intercambiarEstado();
      responderConTeclado(clienteWifi, chatId);
    }
  }
  /*
  Alguien envió un mensaje
  */  if (actualizacion["message"].is<JsonVariant>())
  {
    if (actualizacion["message"]["text"].is<String>())
    {
      /*
      Sin importar el contenido del mensaje, siempre enviamos
      el teclado

      */      String contenido = actualizacion["message"]["text"];
      int idUsuarioRemitente = actualizacion["message"]["from"]["id"];
      responderConTeclado(clienteWifi, idUsuarioRemitente);
    }
  }
}

void loop()
{
  if ((WiFi.status() != WL_CONNECTED))
  {
    parpadearLed(errorWifiEnLoop);
    return;
  }
  String url = "https://api.telegram.org/bot" + tokenTelegram + "/getUpdates?limit=" + String(cantidadActualizacionesPorPeticion);
  if (ultimoIdDeActualizacion != 0)
  {
    url += "&offset=" + String(ultimoIdDeActualizacion + 1);
  }

  if (!clienteHttpActualizaciones.begin(*clienteWifi, url))
  {
    parpadearLed(errorIniciandoClienteActualizaciones);
    return;
  }
  clienteHttpActualizaciones.addHeader("Content-Type", "application/json", false, true);
  clienteHttpActualizaciones.setTimeout(5000);
  int httpCode = clienteHttpActualizaciones.GET();
  if (httpCode > 0)
  {
    String respuestaDelServidor = clienteHttpActualizaciones.getString();
    if (httpCode == HTTP_CODE_OK)
    {
      JsonDocument documentoJson;
      DeserializationError errorAlDecodificarJson = deserializeJson(documentoJson, respuestaDelServidor);
      if (errorAlDecodificarJson)
      {
        parpadearLed(errorDecodificandoJson);
        return;
      }
      JsonArray actualizaciones = documentoJson["result"].as<JsonArray>();
      for (JsonObject actualizacion : actualizaciones)
      {
        manejarActualizacion(actualizacion, clienteWifi);
      }
    }
  }
  clienteHttpActualizaciones.end();
  // Esperar algunos segundos antes de enviar el siguiente mensaje
  delay(milisegundosEsperaEntreActualizaciones);
}

Librerías usadas

Mi archivo platformio.ini queda así:

[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
lib_deps = bblanchon/ArduinoJson@^7.2.1

Instalando drivers y configurando entorno

En caso de que no hayas instalado tu NodeMCU ni le hayas cargado código por favor revisa el siguiente vídeo para una guía:

Token de Telegram e id de chat

En este post asumo que ya tienes tu token del Bot, mismo que el BotFather te debió brindar. Si no, solo habla con él en https://t.me/botfather y crea un nuevo Bot para obtener tu token.

También estoy suponiendo que ya conoces el id de chat, grupo o canal. Si no lo tienes puedes obtener el id de usuario reenviando un mensaje al Bot https://t.me/get_id_bot o https://t.me/JsonDumpBot (no estoy afiliado ni tengo relación con ellos) y estoy seguro de que debe haber distintas maneras de obtenerlo.

Conclusión y pensamientos

Me gusta este método para controlar un foco desde Telegram pues podemos apagarlo y encenderlo desde cualquier parte del mundo y varias plataformas. Al agregar el apagador de 3 vías podemos controlar la bombilla como un foco normal, pero con la posibilidad de hacerlo desde el móvil o cualquier cliente de Telegram.

Lo único complejo de esto es que la NodeMCU ESP8266 no puede sacar 5V para el relevador, por ello necesitamos la fuente de poder externa y el transistor. He hecho esto mismo con una Raspberry Pi Pico W y un módulo de 8 relevadores y ha funcionado perfectamente sin necesidad del transistor.

Como siempre te digo: estas son mis ideas y los circuitos son un poco improvisados, tú puedes mejorarlos y compartir tus ideas en los comentarios.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Servidor HTTP en Android con Flutter

El día de hoy te mostraré cómo crear un servidor HTTP (servidor web) en Android…

3 semanas hace

Imprimir automáticamente todos los PDF de una carpeta

En este post te voy a enseñar a designar una carpeta para imprimir todos los…

3 semanas hace

Guía para imprimir en plugin versión 1 desde Android

En este artículo te voy a enseñar la guía para imprimir en una impresora térmica…

4 semanas hace

Añadir tasa de cambio en sistema de información

Hoy te voy a mostrar un ejemplo de programación para agregar un módulo de tasa…

1 mes hace

Comprobar validez de licencia de plugin ESC POS

Los usuarios del plugin para impresoras térmicas pueden contratar licencias, y en ocasiones me han…

1 mes hace

Imprimir euro € en impresora térmica

Hoy voy a enseñarte cómo imprimir el € en una impresora térmica. Vamos a ver…

1 mes hace

Esta web usa cookies.