Por cuestiones de trabajo suelo ausentarme de mi casa y a pesar de tener sistemas de cámaras uno no siempre esta viendo el móvil para saber que esta pasando es por eso que se me ocurrió un pequeño sistema que conectado a las puertas de acceso me envía un mensaje por Telegram avisando de que tal puerta se ha abierto o cerrado.
El sistema tiene ademas una interfaz web para cambiar la configuración por ejemplo la red Wi-Fi o las credenciales Telegram, para esto un pequeño botón que al ser accionado borra el archivo de configuración y envía un reset a la placa que inicia en modo setup.
Desde luego que para todo esto tenemos que tener instalado en el móvil la aplicación de Telegram.
Lo primero que hacemos es crear un grupo dentro de Telegram y luego mediante el BotFather creamos un bot que agregamos al grupo creado.

El proceso de crear un bot es muy sencillo y la propia aplicación te guía de como hacerlo con el comando /newbot. Pero lo importante aquí es el Token, algo parecido a esto 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 este será el ID del bot y lo necesitará para configurar el acceso y poder enviar mensajes al bot.
Lo siguiente es obtener el ID del usuario, es importante que este bot este agregado al grupo, luego desde un computador enviamos un mensaje al bot y tomamos nota del número ID que aparece en la barra del navegador.
Observe la siguiente imagen donde se puede ver el nombre de mi bot y el ID correspondiente (no olvide el signo menos).

Tenemos los datos necesarios para conectar con Telegram y el bot, ahora solo necesitamos un programa en Pico W que haga el trabajo.
La aplicación que construí puede funcionar de dos formas.

  1. Modo Usuario: Controla el estado de las puertas asignadas.

  2. Modo Programación: El sistema crea una red Wi-Fi propia abierta con el SSID firadmin. En esa red se accede http://firtec.net que básicamente es una página de configuración donde se cargan las credenciales de acceso tanto a la red Wi-Fi adonde se conectará Pico W como las credenciales de acceso a Telegram.

En la siguiente imagen se puede ver el aspecto de la página web de configuración para el sistema.

Una vez que los datos son salvados la red firadmin desaparece y el sistema pasa al modo usuario intentando validar las credenciales de acceso a la red Wi-Fi y cuando sea necesario las credenciales de Telegram.
Para entrar al modo programación se ha colocado un pequeño pulsador en el GPIO_16 que si se oprime el sistema se desconecta de la red Wi-Fi, borra todas las credenciales y pasa a modo programación.
Para detectar la actividad de las puertas se usaron sensores magnéticos que activan una interrupción por cambio de estado en el GPIO_11.

def ISR_11(p):
         global bandera
         global enviado
         if (sensor.value() == 0):
             bandera = 2
             led_azul.value(1)
         if (sensor.value() == 1):
             bandera = 1
             enviado = 0
             led_azul.value(1)
         if (bandera ==1 and enviado == 0):
             send_message(wifi_credentials['telegramDmUid'], 				  text_1)
             enviado = 1
             wdt.feed()   # Borra el WD 
             print("Puerta de calle Abierta!!")
         if (bandera ==2 and enviado == 1):
             wdt.feed()   # Borra el WD 
             send_message(wifi_credentials['telegramDmUid'], 				  text_2)
             print("Puerta de calle Cerrada!!")
             enviado = 3

Para el servidor web y todo lo referente a los procesos web usaremos el módulo phew.

Módulo phew.

Es un pequeño servidor web y una biblioteca de plantillas ya diseñadas específicamente para Micropython en Pico W.
Su objetivo es proporcionar un juego de herramientas completo para crear fácilmente interfaces basadas en la web de alta calidad para todos los proyectos que requieran soporte web.
Es un módulo ideal para crear interfaces de configuración basadas páginas web embebidas que se conectan con sistemas electrónicos.

Que podemos hacer con phew:

  • Un servidor web básico optimizado para la velocidad y recursos de pico w.

  • Uso mínimo de los recursos de memoria y CPU.

  • Motor de plantilla que permite expresiones de Python en línea con los métodos Get y Post.

  • Soporte de conexión Wi-Fi.

Como en ejemplos anteriores lo instalamos en la memoria de pico w usando el administrador de paquetes de Thonny.

Como trabaja el ejemplo propuesto.

Cuando el sistema ha sido configurado crea un archivo que en mi caso he llamado wifi.json y dentro de este archivo encontramos los siguientes datos.

{"ssid": "red_lab","telegramDmUid":"-0100164953574", "password":"1a2b2s2d","botToken": "6146323100:AAGlItE2Mj38mZSRuXxRMRW9t_oR12dYaS4"}

Estos datos son las credenciales de acceso tanto a la red Wi-Fi como al bot de Telegram. Este archivo será borrado automáticamente si se oprime el botón de programación cuando el sistema pasa a modo programación y es por eso que cuando el sistema inicia la primer tarea es verificar que este archivo está en memoria, si no lo encuentra pasa automáticamente al modo programación.
La presencia de este archivo determina en que modo arranca el sistema.
Continuando con el encabezado del programa podemos ver las declaraciones de las variables usadas como el son el punto de acceso que se crea cuando el sistema es programado, el nombre del dominio para acceder a la página de programación etc.

text_1 = 'Puerta de calle abierta!!!'
text_2 = 'Puerta de calle cerrada!!!'
AP_NAME = "firadmin" 		# Nombre del punto de acceso
AP_DOMAIN = "firtec.net" 	# Nombre del dominio
AP_TEMPLATE_PATH = "ap_templates" # Carpetas sitios html
APP_TEMPLATE_PATH = "app_templates" # Carpetas sitios html
WIFI_FILE = "wifi.json" #Donde se guardan las credenciales
LOG_FILE = "log.txt"
WIFI_MAX_ATTEMPTS = 3
bandera = 0
enviado = 0
sensor = Pin(11, Pin.IN, Pin.PULL_UP)
boton = Pin(16, Pin.IN, Pin.PULL_UP)  

Se han colocado unos indicadores LED´s para conocer el estado operativo del sistema. En este caso el indicador verde indica que el sistema está conectado a la red Wi-Fi y el indicador rojo indica que el sistema está en modo programación.

El código completo de la aplicación es el siguiente.

from phew import access_point, connect_to_wifi, is_connected_to_wifi, dns, server
from phew.template import render_template
import json
import machine
import os
import utime
import _thread
import ustruct as struct
from machine import Pin
from machine import WDT
import rp2
import network
import ubinascii
import urequests as requests
import time
 
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wdt = ''
led_rojo = Pin(22, Pin.OUT)
led_rojo.value(0)
red_ok = Pin(21, Pin.OUT)
red_ok.value(0)
led_azul = Pin(20, Pin.OUT)
led_azul.value(0)
 
text_1 = 'Puerta del Quincho Abierta!!!'
text_2 = 'Puerta del Quincho Cerrada!!!'
 
AP_NAME = "firadmin" #Nombre del punto de acceso
AP_DOMAIN = "firtec.net" #Nombre del dominio
AP_TEMPLATE_PATH = "ap_templates"
APP_TEMPLATE_PATH = "app_templates"
WIFI_FILE = "wifi.json" #Donde se guardan las credenciales
LOG_FILE = "log.txt"
WIFI_MAX_ATTEMPTS = 3
bandera = 0
enviado = 0
 
sensor = Pin(11, Pin.IN, Pin.PULL_UP)
boton = Pin(16, Pin.IN, Pin.PULL_UP)  
def ISR_11(p):
         global bandera
         global enviado
         if (sensor.value() == 0):
             bandera = 2
             led_azul.value(1)
         if (sensor.value() == 1):
             bandera = 1
             enviado = 0
             led_azul.value(1)
 
         if (bandera ==1 and enviado == 0):
             send_message(wifi_credentials['telegramDmUid'], text_1)
             enviado = 1
             wdt.feed()   
             print("Puerta de calle Abierta!!")
         if (bandera ==2 and enviado == 1):
             wdt.feed()   
             send_message(wifi_credentials['telegramDmUid'], text_2)
             print("Puerta de calle Cerrada!!")
             enviado = 3
             
sensor.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler= ISR_11)           
 
def machine_reset():
    utime.sleep(1)
    print("Reseteando sistema...")
    machine.reset()
 
def borrar():
        # Borrar el archivo de configuracion y hacer un reboot
        # como acces point.
        os.remove(WIFI_FILE)
        # Reboot en un hilo nuevo
        _thread.start_new_thread(machine_reset, ())
        red_ok.value(0)
          
def setup_mode():
    
    led_rojo.value(1)
    red_ok.value(0)
    print("Entra al modo setup...")
    def ap_index(request):
        if request.headers.get("host").lower() != AP_DOMAIN.lower():
            wdt.feed()   # Borra el WD 
            return render_template(f"{AP_TEMPLATE_PATH}/redirect.html", domain = AP_DOMAIN.lower())
        return render_template(f"{AP_TEMPLATE_PATH}/index.html")
 
    def ap_configure(request):
        print("Salvando las credenciales del wifi...")
        with open(WIFI_FILE, "w") as f:
            json.dump(request.form, f)
            f.close()
        # Reboot en un hilo nuevo
        _thread.start_new_thread(machine_reset, ())
        return render_template(f"{AP_TEMPLATE_PATH}/configured.html", ssid = request.form["ssid"])
        
    def ap_catch_all(request):
        if request.headers.get("host") != AP_DOMAIN:
            return render_template(f"{AP_TEMPLATE_PATH}/redirect.html", domain = AP_DOMAIN)
 
        return "Not found.", 404
    server.add_route("/", handler = ap_index, methods = ["GET"])
    server.add_route("/configure", handler = ap_configure, methods = ["POST"])
    server.set_callback(ap_catch_all)
    ap = access_point(AP_NAME)
    ip = ap.ifconfig()[0]
    dns.run_catchall(ip)
 
#-------- Envía el mensaje a Telegram ---------------
def send_message (chatId, message): 
    wdt.feed()   # Borra el WD -------
    response = requests.post(sendURL + "?chat_id=" + wifi_credentials['telegramDmUid'] + "&text=" + message)
    print('Mensaje Enviado')
    wdt.feed()   # Borra el WD -------
    response.close() # IMPORTANTE!! Cerrar para no desbordar la RAM
 
def application_mode():
    global wdt
    led_azul.value(0)
    wdt = WDT(timeout=8000)  # Habilita el watchdog para 8 Segundos
    wdt.feed()   # Borra el WD 
    led_rojo.value(0)
    global sendURL
    sendURL = 'https://api.telegram.org/bot' + wifi_credentials['botToken'] + '/sendMessage'
    print("Entrando en modo aplicacion.")
    while True:
        wdt.feed()   # Borra el WD 
        led_azul.value(0)
        if (boton.value() == 0 ):
            borrar()
 
# En que modo debe iniciar...
try:
    os.stat(WIFI_FILE)
    with open(WIFI_FILE) as f:
        wifi_current_attempt = 1
        wifi_credentials = json.load(f)
        while (wifi_current_attempt < WIFI_MAX_ATTEMPTS):
            ip_address = connect_to_wifi(wifi_credentials["ssid"], wifi_credentials["password"])
            if is_connected_to_wifi():
                print(f"Conectado con la IP {ip_address}")
                break
            else:
                wifi_current_attempt += 1
        if is_connected_to_wifi():
            red_ok.value(1)
            application_mode()
        else:
            print("ERROR!")
            print(wifi_credentials)
            #os.remove(WIFI_FILE) 
# Esta linea hace que pase al setup si falla wifi habilitar si # queremos que esto pase, 
            machine_reset()
except Exception:
    setup_mode()
server.run()

El archivo index.html tiene el siguiente contenido.

<!DOCTYPE html>
<html>
<meta charset="UTF-8">
    <body style=background:#F0FFFF>
    <FONT COLOR = "black">
    <center>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>SETUP</title>
    </head>
    <body>   
        <h1>Setup del Sistema.</h1>
        <h2>Credenciales de acceso:</h2>
        <form action="/configure" method="POST" autocomplete="off" autocapitalize="none">
            <label for="ssid">Red Wi-Fi:</label><br>
            <input type="text" id="ssid" name="ssid"><br>
            <label for="password">Password:</label><br>
            <input type="text" id="password" name="password"><br>
            <label for="telegramDmUid">Telegram ID:</label><br>
            <input type="text" id="telegramDmUid" name="telegramDmUid"><br>
            <label for="botToken">Token Telegram:</label><br>
            <input type="text" id="botToken" name="botToken"><br><br>
            <button>Salvar los Datos</button>
            <hr Size=5 noshade/><H5><font color='black'>by. Firtec Argentina </H5> 
        </form>
    </body>
    </center>
</html>

En la carpeta ap_templates también vamos a necesitar el archivo configured.py que tiene el siguiente contenido.

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Wifi Configured</title>
    </head>
    <body>
        <h1>Wifi Configured</h1>
        <p>The Raspberry Pi Pico will now reboot and attempt to connect to the &quot;{{ssid}}&quot; wireless network...</p>
    </body>
</html>

También el archivo redirect.html con el contenido.

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Redirigiendo...</title>
        <meta http-equiv="refresh" content="0;url=http://{{domain}}">
    </head>
    <body>
        <p>Redirigiendo...</p>
    </body>
</html>

Recuerde que en wifi_credentials se encuentran las credenciales de acceso tanto para el Wi-Fi como para Telegram.
Cuando el mensaje se envía se extraen las credenciales de la siguiente forma send_message(wifi_credentials['telegramDmUid'], text_1) por ejemplo para el primer mensaje.
El método que envía el mensaje es bastante simple y también lee el Token Telegram desde el archivo de credenciales.
En la siguiente imagen se puede ver la estructura de archivos que debe contener el proyecto.

En lo personal he editado la biblioteca para eliminar la creación del archivo log.txt que eventualmente podría crecer demasiado con el tiempo y ocupar mucha memoria.
Una vez que el sistema esta funcionado los mensajes enviados tienen el siguiente aspecto.

Todo el sistema lleva funcionando mas de un año en distintas puertas sin problemas enviando mensajes a mi móvil.
Si tiene alguna duda puede consultarnos en Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.
Este y otros muchos ejemplos se encuentran en nuestro libro "Electrónica con MicroPython".