Cómo hacer un portal cautivo para su negocio con una Raspberry Pi Pico W
Si diriges una empresa, configurar un portal cautivo puede ser una herramienta importante para tu red. Así que, en este artículo, te mostraré cómo crear un portal cautivo con una Raspberry Pi Pico W.
Un portal cautivo es una forma segura de ofrecer WiFi a invitados, clientes o usuarios. Requiere que los usuarios inicien sesión o se autentiquen antes de poder usar Internet.
Piense en cómo funciona en un aeropuerto o en una cadena de cafeterías.
Un portal cautivo le permite controlar y supervisar su red. También le brinda la oportunidad de extender su marca. Puede personalizar el portal cautivo con su logotipo.
¿Qué hace un portal cautivo?
Cuando te conectas a un Pico W que está actuando como punto de acceso, no se te servirá una página web. Con un portal cautivo implementado, una vez que su WiFi se conecte a su punto de acceso, aparecerá una página web.
Por lo tanto, puede saltarse el paso de conectarse a la red y, a continuación, ir a su navegador y escribir manualmente una dirección IP.
Es en este punto donde tengo que dar crédito a Kevin McAleer por su vídeo que sirve de base para este tutorial.
Crear un portal cautivo con un Pico W es una oportunidad fantástica. Un portal cautivo no sólo es imprescindible para la red de su empresa. A Raspberry Pi Pico W es de unos pocos euros como máximo, por lo que también es una solución muy rentable.
Objetivos de este tutorial
El tutorial de Kevin sólo muestra cómo activar un portal cautivo en un dispositivo Apple.
Este tutorial irá más allá y le explicará cómo activar el portal cautivo emergente en dispositivos Android, Windows y Apple.
Usaremos Phew de Pimoroni, que es una librería para un servidor web Pico W. Es un poco como lo que Express es para NodeJS.
He probado mi código en cuatro dispositivos:
- Apple iPhone 8 con OS versión 15.4.1 ✔️
- Tableta Amazon Fire con Android 9 ✔️
- Windows 10 PC ✔️
- Google Pixel 6 con Android 13 ✔️
- Samsung Note 9 con Android 10 ❌.
El código funcionó en todos excepto en mi Samsung Note 9, y no lo he probado en Linux.
¿Casos prácticos?
Esta es una pregunta un poco difícil.
Inicialmente, quería crear algo que Rickroll alguien, pero me di cuenta de que no puedo incrustar un vídeo de YouTube porque no habrá conexión a Internet ya que el Pico W no tiene conexión a Internet.
Después, quise crear una tarjeta de visita digital portátil en la que la gente se conectara al Pico W y viera algunos enlaces. Algo así como esos directorios de enlaces de Instagram. De nuevo, sin internet...
Supongo que una de las mayores ventajas del Pico W es que permite controlar los componentes de forma inalámbrica.
En nuestro megatutorial de componentesEn el artículo anterior, hablé de cómo puedes evitar el uso de botones y mandos físicos para controlar LEDs, zumbadores y servos utilizando en su lugar una interfaz web. Si implementas esto en tu portal cautivo, podrás controlar más fácilmente tus dispositivos, ya que no tendrás que iniciar sesión en 192.168.4.1 cada vez que quieras acceder a un panel de control.
Por supuesto, me encantaría escuchar lo que crees que es posible con un portal cautivo. Deja un comentario a continuación.
Cómo activar la ventana emergente
Cuando te conectas a una red inalámbrica, los dispositivos Windows, Android y Apple hacen ping a sitios web diferentes.
Cada uno de estos pings requiere una respuesta. La respuesta correcta desbloquea la ventana emergente. Así es como se desarrolla la conversación en función del sistema operativo:
Microsoft Windows 10
MS: "Psst... es /connecttest.txt allí?"
Pico W: "Sí, 200“.
MS: "OK, llévame a /redirigir“
Pico W: "Um, sí, 302Ir a http://pico.wireless/ donde le atenderemos index.html“.
Amazon Fire Android 9
AMZ: "Psst .. ¿dónde está /generar_204 allí?"
Pico W: "Por defecto, iba a decir 204pero sé que lo que realmente quieres es 302 redirigir a http://pico.wireless/ (que renderiza index.html aka tu página cautiva)"
Apple iPhone 8 con iOS 15
iPhone: "Soy guay y todo así que sólo estoy buscando el archivo /detección-de-puntos-hotspot.html“
Pico W: "Sí, ve a http://pico.wireless/“
iPhone: "Hmm, parece completamente diferente de lo que pedí pero confío en ti así que voy a ir allí ahora".
Código de respuesta 200: "OK", 204: "Sin contenido", 302: "Encontrado".
Como puedes ver, los distintos dispositivos piden rutas y páginas web diferentes. Y si respondes correctamente, activarán la ventana emergente.
Aquí tienes un resumen de lo que necesitas para enviar y recibir:
Windows 10
URL | Respuesta desencadenante |
www.msftconnecttest.com/ncsi.txt | 200 OK |
www.msftconnecttest.com/connecttest.txt | 200 OK |
www.msftconnecttest.com/redirect | Redirección 302 (al portal cautivo, por ejemplo, index.html) |
Android 9, 10
URL | Respuesta desencadenante |
connectivitycheck.gstatic.com/generate_204 | Redirección 302 (a la página del portal cautivo) |
Otras versiones de Android podrían consultar otras URL. Creo que las nuevas versiones de Android tendrán /generar_204 como alternativa.
Aquí tienes dos recursos para Android antiguos:
https://lemariva.com/blog/2017/11/white-hacking-wemos-captive-portal-using-micropython
iOS 15
URL | Respuesta desencadenante |
captive.apple.com/hotspot-detect.html | 200 OK (responde con una página web) |
Mi Samsung Note 9 con Android 10 fue el más difícil de descifrar. Aparentemente, había que seguir los patrones típicos de Android, pero nada funcionó.
Conseguí que apareciera el mensaje "Sign-in required", pero sólo después de cambiar el DNS a una IP de LAN pública (una IP fuera del rango '10.0.0.0/8, 172.16.0.0/12, o 192.168.0.0/16'). Véase este comentario de Stack Exchange.
Pero al hacer lo anterior se rompió el portal cautivo de cualquier otro dispositivo...
Biblioteca Pimoroni Phew para el portal cautivo Pico W
Pimoroni La biblioteca facilita enormemente la creación de un portal cautivo porque ya ha hecho todo el trabajo pesado.
En primer lugar, tienes que descargar la biblioteca. Yo utilicé la versión 0.0.3 que puede descargarse aquí.
A continuación, extraiga los archivos y cargue la carpeta denominada uf en su Pico W. Personalmente, he utilizado el IDE Thonny y se puede aprenda a cargar archivos aquí.
Codifiquemos el punto de acceso emergente.
Por lo tanto, permítanme comenzar con main.py:
from phew import logging, server, access_point, dns
from phew.template import render_template
from phew.server import redirect
DOMAIN = "pico.wireless" # This is the address that is shown on the Captive Portal
@server.route("/", methods=['GET'])
def index(request):
""" Render the Index page"""
if request.method == 'GET':
logging.debug("Get request")
return render_template("index.html")
# microsoft windows redirects
@server.route("/ncsi.txt", methods=["GET"])
def hotspot(request):
print(request)
print("ncsi.txt")
return "", 200
@server.route("/connecttest.txt", methods=["GET"])
def hotspot(request):
print(request)
print("connecttest.txt")
return "", 200
@server.route("/redirect", methods=["GET"])
def hotspot(request):
print(request)
print("****************ms redir*********************")
return redirect(f"http://{DOMAIN}/", 302)
# android redirects
@server.route("/generate_204", methods=["GET"])
def hotspot(request):
print(request)
print("******generate_204********")
return redirect(f"http://{DOMAIN}/", 302)
# apple redir
@server.route("/hotspot-detect.html", methods=["GET"])
def hotspot(request):
print(request)
""" Redirect to the Index Page """
return render_template("index.html")
@server.catchall()
def catch_all(request):
print("***************CATCHALL***********************\n" + str(request))
return redirect("http://" + DOMAIN + "/")
# Set to Accesspoint mode
# Change this to whatever Wifi SSID you wish
ap = access_point("Pico W Captive")
ip = ap.ifconfig()[0]
# Grab the IP address and store it
logging.info(f"starting DNS server on {ip}")
# # Catch all requests and reroute them
dns.run_catchall(ip)
server.run() # Run the server
logging.info("Webserver Started")
Para ser claros, adapté este código de Kevin McAleer y lo alteré para nuestros propósitos. Si quieres ver la explicación en profundidad de Kevin sobre cómo funcionan las cosas, vea este vídeo de YouTube.
Pero permítanme repasar brevemente las distintas secciones.
Hay tres secciones con comentarios "#android redirige", "#apple redir" y "# microsoft windows redirige". Estas secciones gestionan las pruebas que realiza cada sistema operativo y responden con la respuesta adecuada.
Al final de las respuestas, se redirigen a http://{DOMAIN}/, excepto la ruta Apple (porque Kevin lo escribió así y si no está roto...).
Redirigir a DOMINIO, declarado como "pico.wireless" nos daría una URL menos antiestética.
Así, en lugar de ver "www.msftconnecttest.com" en la barra de direcciones, veremos "pico.wireless" en la barra de direcciones. Por supuesto, esto es algo a tener en cuenta para el portal cautivo de tu empresa.
Pimoroni Uf reduce el código que tienes que escribir
Lo bueno de Phew es que se escribe bastante menos código que si se escribiera desde cero.
Por ejemplo, si quisieras lanzar el SoftAP en el Pico W, escribirías un código parecido a este:
import network
# start up network in access point mode
wlan = network.WLAN(network.AP_IF)
wlan.config(essid=ssid)
if password:
wlan.config(password=password)
else:
wlan.config(security=0) # disable password
wlan.active(True)
return wlan
Con Phew de Pimoroni, tu carga se reduce a:
ap = access_point("Pico W Captive")
En mi opinión, lo mejor es la dns.run_catchall() que ejecuta un montón de código vital, pero complicado:
import uasyncio, usocket
from . import logging
async def _handler(socket, ip_address):
while True:
try:
yield uasyncio.core._io_queue.queue_read(socket)
request, client = socket.recvfrom(256)
response = request[:2] # request id
response += b"\x81\x80" # response flags
response += request[4:6] + request[4:6] # qd/an count
response += b"\x00\x00\x00\x00" # ns/ar count
response += request[12:] # origional request body
response += b"\xC0\x0C" # pointer to domain name at byte 12
response += b"\x00\x01\x00\x01" # type and class (A record / IN class)
response += b"\x00\x00\x00\x3C" # time to live 60 seconds
response += b"\x00\x04" # response length (4 bytes = 1 ipv4 address)
response += bytes(map(int, ip_address.split("."))) # ip address parts
socket.sendto(response, client)
except Exception as e:
logging.error(e)
def run_catchall(ip_address, port=53):
logging.info("> starting catch all dns server on port {}".format(port))
_socket = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM)
_socket.setblocking(False)
_socket.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1)
_socket.bind(usocket.getaddrinfo(ip_address, port, 0, usocket.SOCK_DGRAM)[0][-1])
loop = uasyncio.get_event_loop()
loop.create_task(_handler(_socket, ip_address))
Phew también facilita la escritura de rutas. Todo lo que tienes que hacer es escribir algo como esto:
@server.route("/[your_route_here]", methods=["GET"])
def your_function_here(request):
pass
@server.catchall()
def catchall(request):
pass
Como puedes ver arriba, crear una ruta es superfácil. Todo lo que necesitas hacer es utilizar @servidor.rutay pasa una ruta así como los métodos. A continuación, defina una función a continuación con el solicitar parámetro.
Por último, está el @server.catchall() que se encarga de todas las rutas que no hayas asignado.
Uf, ¡es muy fácil!
Visite el repositorio Github de Phew aquí.
index.html
Es una simple prueba de concepto index.html que emite un <h1> diciendo "Pico W Portal Cautivo".
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pico W Captive Portal</title>
</head>
<body>
<h1>Pico W Captive Portal</h1>
</body>
</html>
Conclusión:
Ahí lo tienes. Eso es todo lo que necesita saber para configurar un portal cautivo en un Pico W para su negocio.
Si tiene alguna pregunta, no dude en hacérnosla llegar en los comentarios.
Un buen sustituto para un piratebox (aunque sin función de chat y carga).
Esto es exactamente lo que estaba buscando.
La mayoría de las cosas funcionan, pero el sitio web no aparece por sí solo, sino que aparece un error:
2023-03-31 12:02:35 [info / 160kB] > GET /generate_204 (302 Found) [236ms]
No se ha recuperado la excepción de tarea
futuro: coro=
Traceback (última llamada más reciente):
File "uasyncio/core.py", line 1, in run_until_complete
File "phew/server.py", line 242, in _handle_request
File "phew/server.py", line 161, in _parse_headers
ValueError: necesito más de 1 valores para desempaquetar
Me pregunto si tal vez tiene algo que ver con mi viejo teléfono sigue siendo Android 6..
¿Cómo se edita ese código para incluir el código HTML dentro del programa? Lo he intentado y no me ha funcionado.
El flujo del programa me parece muy confuso.
Hola, encantado de ayudarle. ¿Podría describir su problema con más detalle y decirme qué dispositivo está utilizando para el portal cautivo?
¡Hola! Estoy tratando de ejecutar esto en un Pico W.. el AP se inicia, el DNS se inicia, el servidor web o bien nunca se inicia o tomar un tiempo looooooong (varios minutos) para iniciar. ¿Tiene alguna idea / pistas / sugerencias en cuanto a por qué? Gracias.
¿Puede ayudarme a entender qué línea de código ejecuta/automatiza la apertura del portal cautivo en el navegador del cliente, después de una conexión exitosa al hotspot? No parece abrir nada automáticamente. Me conecto desde mi portátil.
Gracias por la buena descripción.
Me gustaría añadir un segundo o incluso un tercer SSID al mismo AP. ¿Hay alguna forma de hacerlo con Phew?
¿Alguna idea?
No, tendrías que usar una raspberry pi pico para cada ssid.