Como criar um portal cativo para a sua empresa com um Raspberry Pi Pico W
Se estiver a gerir uma empresa, então a criação de um portal cativo pode ser uma ferramenta importante para a sua rede. Por isso, neste artigo, vou mostrar-lhe como criar um portal cativo com um Raspberry Pi Pico W.
Um portal cativo fornece uma forma segura de oferecer WiFi a convidados, clientes ou consumidores. Exige que os utilizadores iniciem sessão ou se autentiquem antes de poderem utilizar a Internet.
Pense em como funciona num aeroporto ou numa cadeia de cafés.
Um portal cativo permite-lhe controlar e monitorizar a sua rede. Também proporciona uma oportunidade para alargar a sua marca. Pode personalizar o portal cativo com o seu logótipo.
O que faz um portal cativo?
Quando se liga a um Pico W que está a funcionar como ponto de acesso, não lhe é apresentada uma página Web. Com um portal cativo implementado, assim que o WiFi se ligar ao seu ponto de acesso, aparecerá uma página Web.
Por conseguinte, pode saltar a etapa de ligação à rede e, em seguida, aceder ao seu browser e introduzir manualmente um endereço IP.
É nesta altura que tenho de dar crédito Kevin McAleer pelo seu vídeo que serve de base a este tutorial.
Criar um portal cativo com um Pico W é uma oportunidade fantástica. Não só um portal cativo é imperativo para a rede da sua empresa. A Framboesa Pi Pico W é de apenas alguns euros no máximo, pelo que é também uma solução extremamente económica.
Objectivos para este tutorial
O tutorial do Kevin apenas mostra como acionar um portal cativo num dispositivo Apple.
Este tutorial irá mais longe, explicando-lhe como acionar o portal cativo pop-up em dispositivos Android, Windows e Apple.
Vamos usar o Phew da Pimoroni, que é uma biblioteca para um servidor web Pico W. É um pouco como o Express é para o NodeJS.
Testei o meu código em quatro dispositivos:
- Apple iPhone 8 com versão do SO 15.4.1 ✔️
- Tablet Amazon Fire com Android 9 ✔️
- Windows 10 PC ✔️
- Google Pixel 6 com Android 13 ✔️
- Samsung Note 9 com Android 10 ❌
O código funcionou em todos eles, exceto no meu Samsung Note 9, e não o testei no Linux.
Casos de utilização?
Esta é uma pergunta um pouco difícil.
Inicialmente, queria criar algo que pudesse fazer o Rickroll de alguém, mas apercebi-me de que não podia inserir um vídeo do YouTube porque não haveria ligação à Internet, uma vez que o Pico W não tem ligação à Internet.
Depois, quis criar um cartão de visita digital portátil em que as pessoas se ligassem ao Pico W e vissem algumas ligações. Uma espécie de diretório de links na biografia do Instagram. Mais uma vez, sem internet...
Suponho que uma das maiores vantagens do Pico W é o facto de permitir controlar os componentes sem fios.
Em o nosso mega-tutorial de componentesNo artigo "A interface web", falei sobre a forma de evitar a utilização de botões físicos para controlar LEDs, buzzers e servos, utilizando uma interface web. Se as implementar no seu portal cativo, poderá controlar mais facilmente os seus dispositivos, uma vez que não terá de iniciar sessão em 192.168.4.1 sempre que quiser aceder a um painel de controlo.
Claro que gostaria de saber o que pensa que é possível fazer com um portal cativo. Deixe um comentário abaixo!
Como acionar o pop-up
Quando se liga a uma rede sem fios, os dispositivos Windows, Android e Apple fazem ping a diferentes sítios Web.
Cada um destes pings exige uma resposta. A resposta correcta desbloqueia o pop-up. Eis como decorre a conversa consoante o SO:
Microsoft Windows 10
MS: "Psst... é /connecttest.txt lá?"
Pico W: "Sim, 200“.
MS: "OK, leva-me até /redirecionar“
Pico W: "Hum, sim, 302, ir para http://pico.wireless/ onde o servimos index.html“.
Amazon Fire Android 9
AMZ: "Psst... onde está o /gerar_204 lá?"
Pico W: "Por defeito, eu ia dizer 204mas sei que o que realmente queres é 302 redirecionar para http://pico.wireless/ (que processa index.html, ou seja, a sua página cativa)"
Apple iPhone 8 com iOS 15
iPhone: "Eu sou fixe e tudo, por isso só estou à procura do ficheiro /hotspot-detect.html“
Pico W: "Sim, vai para http://pico.wireless/“
iPhone: "Hmm, parece completamente diferente do que pedi, mas confio em ti, por isso vou lá agora."
Código de resposta 200: "OK", 204: "Sem conteúdo", 302: "Encontrado".
Como pode ver, diferentes dispositivos pedem diferentes percursos e diferentes páginas Web. E, se responder corretamente, eles accionam o pop-up.
Eis um resumo do que é necessário para enviar e receber:
Janelas 10
URL | Resposta de acionamento |
www.msftconnecttest.com/ncsi.txt | 200 OK |
www.msftconnecttest.com/connecttest.txt | 200 OK |
www.msftconnecttest.com/redirect | 302 redirecionar (para o portal cativo, por exemplo, index.html) |
Android 9, 10
URL | Resposta de acionamento |
connectivitycheck.gstatic.com/generate_204 | Redireccionamento 302 (para a página do portal cativo) |
Outras versões do Android podem consultar outros URLs. Creio que as versões mais recentes do Android terão /gerar_204 como alternativa.
Aqui estão dois recursos para Androids mais antigos:
https://lemariva.com/blog/2017/11/white-hacking-wemos-captive-portal-using-micropython
iOS 15
URL | Resposta de acionamento |
captive.apple.com/hotspot-detect.html | 200 OK (responde com uma página Web) |
O meu Samsung Note 9 com Android 10 foi o mais difícil de resolver. Ostensivamente, seguir-se-iam os padrões típicos do Android mas, infelizmente, nada funcionou!
Consegui obter o aviso "É necessário iniciar sessão", mas só depois de alterar o DNS para um IP de LAN público (um IP fora do intervalo '10.0.0.0/8, 172.16.0.0/12 ou 192.168.0.0/16'). Ver este comentário do Stack Exchange.
Mas o procedimento acima quebrou o portal cativo de todos os outros dispositivos...
Biblioteca Pimoroni Phew para o portal cativo do Pico W
Pimoroni's Phew A biblioteca torna muito mais fácil a criação de um portal cativo, porque já fizeram todo o trabalho pesado.
Em primeiro lugar, é necessário descarregar a biblioteca. Eu utilizei a versão 0.0.3 que pode ser descarregado aqui.
Em seguida, extraia os ficheiros e carregue a pasta com o nome ufa no seu Pico W. Pessoalmente, utilizei o Thonny IDE e pode saiba como carregar ficheiros aqui.
Vamos codificar o pop-up do hotspot!
Por isso, deixem-me começar por 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 claro, adaptei este código de Kevin McAleer e alterei-o para os nossos objectivos. Se quiseres ver a explicação detalhada do Kevin sobre como as coisas funcionam, ver este vídeo do YouTube.
Mas permitam-me que passe brevemente em revista as diferentes secções.
Existem três secções com comentários "#android redirects", "#apple redir" e "# microsoft windows redirects". Estas secções tratam dos testes que cada sistema operativo faz e respondem com a resposta certa.
No final das respostas, são redireccionadas para http://{DOMAIN}/, exceto para a rota da Apple (porque o Kevin a escreveu dessa forma e se não está estragada...).
Redirecionar para DOMAIN, declarado como "pico.wireless", dar-nos-ia um URL menos desagradável.
Assim, em vez de vermos "www.msftconnecttest.com" na barra de endereço, veremos "pico.wireless" na barra de endereço. Naturalmente, isto é algo a ter em conta no portal cativo da sua empresa.
Pimoroni Phew reduz o código que é necessário escrever
A vantagem do Phew é que escreve muito menos código do que se o escrevesse de raiz.
Por exemplo, se quisesse lançar o SoftAP no Pico W, escreveria um código mais ou menos assim:
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
Com o Phew da Pimoroni, o seu fardo fica reduzido a:
ap = access_point("Pico W Captive")
Na minha opinião, a melhor parte é a dns.run_catchall() que executa um monte de código vital, mas 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))
O Phew também facilita a criação de itinerários. Tudo o que tem de fazer é escrever algo como isto:
@server.route("/[your_route_here]", methods=["GET"])
def your_function_here(request):
pass
@server.catchall()
def catchall(request):
pass
Como pode ver acima, criar uma rota é muito fácil. Tudo o que precisa de fazer é utilizar @server.routee passe uma rota, bem como os métodos. Em seguida, defina uma função abaixo com o pedido parâmetro.
Por fim, há o @server.catchall() que trata de todos os itinerários que não foram atribuídos.
Ufa, é muito fácil!
Visite o repositório Github do Phew aqui.
index.html
Trata-se de uma prova de conceito muito simples index.html que emite um <h1> dizendo "Pico W Captive Portal".
<!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>
Conclusão
Aqui tem. É tudo o que precisa de saber para configurar um portal cativo num Pico W para a sua empresa.
Se tiver alguma dúvida, não hesite em contactar-nos nos comentários abaixo!
Um bom substituto para uma piratebox (embora sem a função de conversação e carregamento).
Isto é exatamente o que eu estava à procura!
A maior parte das coisas funciona, mas o sítio Web não está a aparecer sozinho e, em vez disso, vejo um erro:
2023-03-31 12:02:35 [info / 160kB] > GET /generate_204 (302 Encontrado) [236ms]
A exceção da tarefa não foi recuperada
futuro: coro=
Traceback (última chamada):
Ficheiro "uasyncio/core.py", linha 1, in run_until_complete
Ficheiro "phew/server.py", linha 242, em _handle_request
Ficheiro "phew/server.py", linha 161, em _parse_headers
ValueError: precisa de mais de 1 valores para descompactar
Será que tem alguma coisa a ver com o facto de o meu telemóvel antigo ainda ser Android 6?
Como é que se pode editar esse código para incluir o código HTML dentro do programa? Tentei e não funcionou.
Considero o fluxo do programa muito confuso.
Olá! Tenho todo o gosto em ajudar! Pode descrever o seu problema com mais pormenor e dizer-me qual o dispositivo que está a utilizar para o portal cativo?
Olá! Estou a tentar executar isto num Pico W... o AP arranca, o DNS arranca, o servidor Web nunca arranca ou demora muuuuito tempo (vários minutos) a arrancar. Têm alguma ideia, pista ou sugestão sobre o motivo? Obrigado!
Podem ajudar-me a perceber qual é a linha de código que impõe/auto-abre o portal cativo no browser do cliente, após uma ligação bem sucedida ao hotspot? Não parece abrir automaticamente nada. Estou a ligar-me a partir do meu computador portátil.
Obrigado pela boa descrição.
Gostaria de adicionar um segundo ou mesmo um terceiro SSID ao mesmo AP. Existe alguma forma de o fazer com o Phew?
Alguma opinião?
Não, teria de utilizar um raspberry pi pico para cada ssid.