En mi blog ya te he enseñado a controlar un foco usando un Arduino con Bluetooth y también una NodeMCU ESP8266 a través de un Bot de Telegram.

Hoy te quiero contar sobre un proyecto de controlar todas las bombillas de una casa usando:

  • Bot de Telegram
  • Raspberry Pi Pico W
  • Relevadores
  • Apagadores de escalera
Chat de Telegram con el Bot para controlar varias bombillas usando Raspberry Pi Pico W. Se muestran algunos focos encendidos usando emojis distintos

Con esto vas a poder controlar todos los focos de una casa ya sea con interruptores normales (los físicos que usamos normalmente) pero también con el móvil.

Gracias a que vamos a usar un Bot de Telegram podemos hacer que varias personas controlen las luces desde cualquier lugar del mundo con internet.

Explicación de circuitos

El requerimiento era poder controlar los focos de una casa completa usando un Bot de Telegram pero también poder hacerlo manualmente.

Para ello hay que usar apagadores de 3 vías. Los apagadores de 3 vías también llamados apagadores de escaleras permiten controlar el mismo foco desde 2 lugares distintos.

Lo que se hizo aquí fue dejar una vía en cada apagador físico y luego dejar la otra vía en el lugar donde iba a estar la RPI.

Es decir, si había 6 focos para controlar entonces dejábamos un interruptor físico cerca de cada foco con 1 vía de las 2 disponibles, y la segunda vía la dejábamos agrupada junto con las otras 5.

De este modo iba a haber “6 puntas” de cable en el mismo lugar, que es justamente donde se conectarán los relevadores y todo el circuito.

Circuito de conexión para controlar varias bombillas usando una Raspberry Pi Pico W. Se aprecia la conexión de la bombilla al apagador de 3 vías y al relevador, indicando que dicha conexión se debe repetir por cada bombilla. También se aprecia la conexión de los puertos GPIO de la RPI a los relevadores

Ya hablando de la conexión:

  • Conectar fuente de alimentación DC 5V a la Raspberry Pi Pico W y al módulo de relevadores
  • Por cada foco que queremos controlar debemos conectar un foco en los relevadores
  • Cada conexión del foco es: Neutro de alimentación doméstica AC a una punta del foco. Fase de alimentación doméstica AC al común del apagador de escalera. Común del relevador a la punta restante del foco. L1 del apagador de escalera al NO del relevador. L2 del apagador de escalera al NC del relevador.
  • Debemos repetir esa conexión por cada foco

Código fuente

Está escrito en MicroPython. Para subirlo utilizo Thonny. El código hace que la Raspberry Pi Pico W se conecte a la red WiFi y que lo reintente en caso de desconexión.

Luego simplemente hace peticiones HTTP a la API de Telegram. Si detecta que alguien tocó un botón entonces revisa el índice e intercambia el estado del relevador que está conectado a uno de sus pines GPIO para finalmente volver a enviar ese mismo teclado pero con el indicador de encendido o apagado.

También permite controlar los usuarios que pueden controlar el bot a través del arreglo USUARIOS_PERMITIDOS. Puedes obtener el id de usuario de varias maneras, yo recomiendo @JsonDumpBot (no tengo nada que ver con él ni con su desarrollador)

Finalmente se utiliza el LED incorporado para indicar el estado. Si está verde sin parpadear entonces la conexión a WiFi ha sido correcta. Si no, va a parpadear al estar conectando


import network
import time
import urequests
import ujson
from machine import Pin
TIMEOUT_PETICIONES = 5
LIMITE_UPDATES_TELEGRAM = 1
TOKEN_TELEGRAM = "Aquí tu bot de Telegram"
URL_BOT_TELEGRAM = "https://api.telegram.org/bot" + TOKEN_TELEGRAM + \
    "/getUpdates?limit=" + str(LIMITE_UPDATES_TELEGRAM)
ultimo_id_actualizacion = 0
COMANDO_INICIAR = "/start"
INDICADOR_ENCENDIDO = "💡"
INDICADOR_APAGADO = "⚫"
USUARIOS_PERMITIDOS = [123456789] # Aquí coloca los id de los usuarios que quieres que controlen el bot
NOMBRE_WIFI = "Nombre de tu red"
PALABRA_SECRETA_WIFI = "Contraseña de tu red"
LED_ESTADO = Pin("LED", Pin.OUT)
HEADERS_HTTP = {'content-type': 'application/json; charset=utf-8'}
URL_ENVIAR_MENSAJE = "https://api.telegram.org/bot" + \
    TOKEN_TELEGRAM + "/sendMessage"
"""
Cuando la tarjeta se reinicia,
la sala (Pin 1) se enciende, así
que su valor debe ser 1
Lo mismo pasa con el exterior (pin 2)
"""
focos = {
    "0": {
        "relevador": Pin(0, Pin.OUT),
        "etiqueta": "🎮 Habitación norte",
        "invertido": False,
    },
    "1": {
        "relevador": Pin(1, Pin.OUT, value=1),
        "etiqueta": "🛋 Sala",
        "invertido": True,
    },
    "2": {
        "relevador": Pin(2, Pin.OUT, value=1),
        "etiqueta": "🌲 Exterior",
        "invertido": True,
    },
    "3": {
        "relevador": Pin(3, Pin.OUT),
        "etiqueta": "👩‍🏫 Habitación sur",
        "invertido": False,
    },
    "4": {
        "relevador": Pin(4, Pin.OUT),
        "etiqueta": "🍕 Cocina",
        "invertido": False,
    },
    "5": {
        "relevador": Pin(5, Pin.OUT),
        "etiqueta": "🛁 Baño",
        "invertido": False,
    },
}
red_wifi = network.WLAN(network.STA_IF)
red_wifi.active(True)


def conectar_wifi_si_esta_desconectado():
    if red_wifi.isconnected():
        return
    LED_ESTADO.off()

    red_wifi.active(True)  
    time.sleep(1)        
    for _ in range(5):
        try:
            red_wifi.connect(NOMBRE_WIFI, PALABRA_SECRETA_WIFI)
            for _ in range(50):
                if red_wifi.isconnected():
                    LED_ESTADO.on()
                    return
                LED_ESTADO.toggle()
                time.sleep(0.1)
        except:
            pass
        time.sleep(1)  



def enviar_mensaje_denegado(id_chat):
    carga_util = ujson.dumps(
        {'chat_id': id_chat, 'text': 'Lo siento, no tienes permisos. Si esto es un error, informa que tu id es ' + str(id_chat)}).encode("utf-8")
    respuesta_http = urequests.post(
        URL_ENVIAR_MENSAJE, headers=HEADERS_HTTP, data=carga_util, timeout=TIMEOUT_PETICIONES)
    respuesta_http.close()


def responder_con_teclado_para_controlar_focos(id_chat):
    teclado = []
    for indice_foco in focos:
        foco = focos[indice_foco]
        indicador = INDICADOR_APAGADO
        if foco["relevador"].value():
            indicador = INDICADOR_ENCENDIDO
        if foco["invertido"]:
            if indicador == INDICADOR_APAGADO:
                indicador = INDICADOR_ENCENDIDO
            else:
                indicador = INDICADOR_APAGADO
        teclado.append([{"text": foco["etiqueta"] + "\n " +
                       indicador, "callback_data": indice_foco}])
    post_data = ujson.dumps(
        {'chat_id': id_chat, 'text': 'Utiliza ' + COMANDO_INICIAR + " cuando quieras para ver el estado actual o toca cualquier botón para intercambiar su estado", 'reply_markup': {"inline_keyboard": teclado}}).encode("utf-8")
    respuesta_http = urequests.post(
        URL_ENVIAR_MENSAJE, headers=HEADERS_HTTP, data=post_data, timeout=TIMEOUT_PETICIONES)
    respuesta_http.close()


def id_usuario_valido(id_usuario):
    if id_usuario in USUARIOS_PERMITIDOS:
        return True
    return False


def manejar_actualizaciones_del_bot():
    global ultimo_id_actualizacion
    url = URL_BOT_TELEGRAM
    if ultimo_id_actualizacion is not 0:
        url += "&offset=" + str(ultimo_id_actualizacion + 1)
    respuesta_http = urequests.get(url, timeout=TIMEOUT_PETICIONES)
    respuesta_telegram = respuesta_http.json()
    if not respuesta_telegram["ok"]:
        respuesta_http.close()
        return
    actualizaciones = respuesta_telegram["result"]
    for actualizacion in actualizaciones:
        if "callback_query" in actualizacion:
            id_usuario = actualizacion["callback_query"]["message"]["chat"]["id"]
            if not id_usuario_valido(id_usuario):
                enviar_mensaje_denegado(id_usuario)
            else:
                indice = actualizacion["callback_query"]["data"]
                focos[indice]["relevador"].toggle()
                responder_con_teclado_para_controlar_focos(
                    actualizacion["callback_query"]["message"]["chat"]["id"])
        if "message" in actualizacion:
            id_usuario = actualizacion["message"]["chat"]["id"]
            if not id_usuario_valido(id_usuario):
                enviar_mensaje_denegado(id_usuario)
            else:
                if actualizacion["message"]["text"] == COMANDO_INICIAR:
                    responder_con_teclado_para_controlar_focos(
                        actualizacion["message"]["chat"]["id"])
        ultimo_id_actualizacion = actualizacion["update_id"]
    respuesta_http.close()


def manejar():
    try:
        conectar_wifi_si_esta_desconectado()
        manejar_actualizaciones_del_bot()
    except Exception as e:
        print(e)
        pass


while True:
    manejar()

Actualizando código más adelante

Como la RPI se conecta directamente a la energía de la casa usando una fuente convertidora no es recomendable conectar el cable USB, así que cada vez que se desea actualizar el código de la tarjeta se debe desconectar la energía eléctrica y luego seguir el siguiente proceso.

  1. Conectar Rasberry Pi Pico W a laptop usando cable USB
  2. Abrir Thonny en la laptop
  3. Fijarnos que hasta abajo diga MicroPython (Raspberry Pi Pico) Board CDC @COMX donde X es un número
  4. Volver a seleccionar dicha opción del punto 3 aunque ya esté seleccionada

Se debe ver así:

Editor de código Thonny con código MicroPython listo para programar Raspberry Pi Pico W con sketch de control de bombillas

Fíjate que hasta abajo aparece algo como un REPL y dice:

MicroPython v1.19.1-852-g9ea64a36a on 2023-02-03; Rasberry Pi Pico W with RP2040

Una buena señal es que cuando hagamos clic en Guardar va a preguntar si queremos guardar en Este computador o en Rasberry Pi Pico

Cuando hayamos guardado comprobamos que se conecte a la red. Como no habrá energía eléctrica de la verdadera red podemos crear un AP con nuestro teléfono; una vez que se conecta entonces ya podemos desconectar todo de la laptop y levantar la pastilla térmica que controla toda la alimentación de la casa.

Si presenta errores como Device is busy – can’t perform this action now. Please wait or cancel current work and try again! es simplemente detener la ejecución con el botón de Thonny o presionando CTRL + C, así como desconectando y conectando el cable USB de la RPI Pico W.

Si el post ha sido de tu agrado te invito a que me sigas para saber cuando haya escrito un nuevo post, haya actualizado algún sistema o publicado un nuevo software. Facebook | X | Instagram | Telegram | También estoy a tus órdenes para cualquier contratación en mi página de contacto