Como criar um portal cativo para a sua empresa com um Raspberry Pi Pico W

portal cativo de framboesa pi pico w criado

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

URLResposta de acionamento
www.msftconnecttest.com/ncsi.txt200 OK
www.msftconnecttest.com/connecttest.txt200 OK
www.msftconnecttest.com/redirect302 redirecionar (para o portal cativo, por exemplo, index.html)

Android 9, 10

URLResposta de acionamento
connectivitycheck.gstatic.com/generate_204Redireccionamento 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

https://enterprisenetworkingatlarge.wordpress.com/2018/04/21/captive-portal-detection-on-android-and-others-client-vendors-vs-ap-vendors/

iOS 15

URLResposta de acionamento
captive.apple.com/hotspot-detect.html200 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.

No final do tutorial, deverá ter um diretório com o seguinte aspeto.

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!

8 comentários

  1. cinos em Janeiro 8, 2023 às 4:11 pm

    Um bom substituto para uma piratebox (embora sem a função de conversação e carregamento).

  2. OkinKun em Março 31, 2023 às 5:07 pm

    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?

  3. Dingleberry em Junho 21, 2023 às 10:57 pm

    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.

    • Adam Bobeck em Junho 26, 2023 às 2:51 pm

      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?

  4. NewbiePicoWHacker em Outubro 1, 2023 às 3:36 pm

    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!

  5. Prithwiraj Bose em Novembro 5, 2023 às 3:43 pm

    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.

  6. Peter em Novembro 24, 2023 às 8:13 pm

    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?

    • Toby em Maio 28, 2024 às 5:13 pm

      Não, teria de utilizar um raspberry pi pico para cada ssid.

Deixe um comentário