Zo maak je een Captive Portal voor je bedrijf met een Raspberry Pi Pico W

raspberry pi pico w captive portal instellen

Als je een bedrijf runt, dan kan het opzetten van een captive portal een belangrijk hulpmiddel zijn voor je netwerk. In dit artikel laat ik je zien hoe je een captive portal kunt maken met een Raspberry Pi Pico W.

Een captive portal biedt een veilige manier om WiFi aan te bieden aan gasten, klanten of cliënten. Het vereist dat gebruikers inloggen of zich authenticeren voordat ze het internet kunnen gebruiken.

Denk aan hoe het werkt op een vliegveld of bij een koffieshopketen.

Met een captive portal kun je je netwerk zowel controleren als monitoren. Het biedt ook de mogelijkheid om je merk uit te breiden. Je kunt de captive portal aanpassen met je logo.

Wat doet een captive portal?

Wanneer je inlogt op een Pico W die als toegangspunt fungeert, krijg je geen webpagina te zien. Met een captive portal geïmplementeerd, zal er een webpagina verschijnen zodra je WiFi verbinding maakt met je hotspot.

Daarom kun je de stap van verbinding maken met het netwerk overslaan, waarna je naar je browser gaat en handmatig een IP-adres intypt.

Het is op dit punt dat ik het volgende moet vermelden Kevin McAleer voor zijn video die als basis dient voor deze tutorial.

Het creëren van een captive portal met een Pico W is een fantastische mogelijkheid. Niet alleen is een captive portal noodzakelijk voor het netwerk van je bedrijf. A Raspberry Pi Pico W is hooguit een paar euro, dus het is ook een zeer kosteneffectieve oplossing.

Doelen voor deze tutorial

Kevin's tutorial laat alleen zien hoe je een captive portal activeert op een Apple apparaat.

Deze tutorial gaat verder door uit te leggen hoe je de pop-up captive portal activeert op Android, Windows en Apple apparaten.

We gebruiken Phew van Pimoroni, een bibliotheek voor een Pico W webserver. Het is een beetje wat Express is voor NodeJS.

Ik heb mijn code op vier apparaten getest:

  • Apple iPhone 8 met OS versie 15.4.1 ✔️
  • Amazon Fire-tablet met Android 9 ✔️
  • Windows 10 PC ✔️
  • Google Pixel 6 met Android 13 ✔️
  • Samsung Note 9 met Android 10 ❌

De code werkte op allemaal, behalve op mijn Samsung Note 9, en ik heb het niet getest op Linux.

Gebruiksgevallen?

Dit is een lastige.

In eerste instantie wilde ik iets maken dat iemand zou Rickrollen, maar ik realiseerde me dat ik geen YouTube-video kan insluiten omdat er geen internetverbinding is omdat de Pico W geen internetverbinding heeft.

Vervolgens wilde ik een draagbaar digitaal visitekaartje maken waarop mensen verbinding kunnen maken met de Pico W en een paar links kunnen zien. Een beetje zoals die Instagram link-in-bio mappen. Nogmaals, geen internet...

Ik denk dat een van de grootste voordelen van de Pico W is dat je er componenten draadloos mee kunt bedienen.

In onze mega-handleiding voor componentenheb ik het gehad over hoe je het gebruik van fysieke knoppen en draaiknoppen kunt vermijden om LED's, zoemers en servo's te bedienen door in plaats daarvan een webinterface te gebruiken. Als je deze implementeert in je captive portaal, kun je je apparaten eenvoudiger bedienen omdat je niet elke keer hoeft in te loggen op 192.168.4.1 als je naar een bedieningspaneel wilt gaan.

Natuurlijk hoor ik graag wat jij denkt dat er mogelijk is met een captive portal. Laat hieronder een reactie achter!

De pop-up activeren

Wanneer je verbinding maakt met een draadloos netwerk, pingen Windows-, Android- en Apple-apparaten verschillende websites.

Elk van deze pings vereist een antwoord. Het juiste antwoord ontgrendelt de pop-up. Dit is hoe het gesprek verloopt, afhankelijk van het besturingssysteem:

Microsoft Windows 10

MS: "Psst... is /connecttest.txt daar?"

Pico W: "Ja, 200“.

MS: "Oké, breng me naar /redirect

Pico W: "Eh, ja, 302ga naar http://pico.wireless/ waar we u van dienst zijn index.html“.

Amazon Fire Android 9

AMZ: "Psst... waar is /genereren_204 daar?"

Pico W: "Standaard zou ik zeggen 204maar ik weet dat wat je echt wilt is 302 doorverwijzen naar http://pico.wireless/ (die index.html aka je captive pagina weergeeft)".

Apple iPhone 8 met iOS 15

iPhone: "Ik ben cool en zo, dus ik ben gewoon op zoek naar het bestand". /hotspot-detect.html

Pico W: "Ja, ga naar http://pico.wireless/

iPhone: "Hmm, lijkt heel anders dan waar ik om vroeg, maar ik vertrouw je dus ik ga er nu heen."

Responscode 200: "OK", 204: "Geen inhoud", 302: "Gevonden".

Zoals je kunt zien, vragen verschillende apparaten om verschillende routes en verschillende webpagina's. En als je correct antwoordt, activeren ze de pop-up.

Hier volgt een overzicht van wat je nodig hebt om te verzenden en te ontvangen:

Windows 10

URLInitiërende reactie
www.msftconnecttest.com/ncsi.txt200 OK
www.msftconnecttest.com/connecttest.txt200 OK
www.msftconnecttest.com/redirect302 doorsturen (naar captive portaal, bijv. index.html)

Android 9, 10

URLInitiërende reactie
connectiviteitscontrole.gstatic.com/genereren_204302 doorsturen (naar gesloten portaalpagina)

Andere Android-versies kunnen andere URL's opvragen. Ik geloof dat de nieuwere Android-versies /genereren_204 als noodoplossing.

Hier zijn twee bronnen voor oudere Androids:

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

URLInitiërende reactie
captive.apple.com/hotspot-detect.html200 OK (antwoorden met een webpagina)

Mijn Samsung Note 9 met Android 10 was het moeilijkst te kraken. Ogenschijnlijk zou je de typische Android-patronen volgen, maar helaas, niets werkte!

Het lukte me wel om de "Sign-in required" prompt te krijgen, maar alleen nadat ik de DNS had gewijzigd naar een publiek LAN IP (een IP buiten het '10.0.0.0/8, 172.16.0.0/12, of 192.168.0.0/16' bereik). Zie dit commentaar op Stack Exchange.

Maar door het bovenstaande te doen, werd de gesloten portal van elk ander apparaat verbroken...

Pimoroni Phew-bibliotheek voor Pico W gesloten portaal

Pimoroni's Oef bibliotheek maakt het veel gemakkelijker om een gesloten portaal te maken omdat zij al het zware werk hebben gedaan.

Eerst moet je de bibliotheek downloaden. Ik heb de versie 0.0.3 gebruikt die hier kan worden gedownload.

Pak vervolgens de bestanden uit en upload de map met de naam phew op je Pico W. Persoonlijk heb ik de Thonny IDE gebruikt en je kunt Lees hier hoe u bestanden uploadt.

Aan het einde van de tutorial zou je een map moeten hebben die er zo uitziet.

Laten we de hotspot pop-up coderen!

Dus laat ik beginnen met 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")

Om duidelijk te zijn, ik heb deze code van Kevin McAleer aangepast voor onze doeleinden. Als je Kevin's uitgebreide uitleg over hoe het werkt wilt bekijken, bekijk deze YouTube-video.

Maar laat me kort de verschillende secties bespreken.

Er zijn drie secties met opmerkingen "#android redirects", "#apple redir" en "# microsoft windows redirects". Deze secties behandelen de tests die elk besturingssysteem uitvoert en antwoorden met het juiste antwoord.

Aan het einde van de reacties worden ze doorgestuurd naar http://{DOMAIN}/, behalve voor de Apple route (omdat Kevin het zo geschreven heeft en als het niet kapot is...).

Doorverwijzen naar DOMAIN, opgegeven als "pico.wireless" zou ons een minder lelijke URL geven.

Dus in plaats van "www.msftconnecttest.com" in de adresbalk, zien we "pico.wireless" in de adresbalk. Dit is natuurlijk iets om in gedachten te houden voor de captive portal van je bedrijf.

Pimoroni Phew vermindert de code die je moet schrijven

Het mooie van Phew is dat je aanzienlijk minder code schrijft dan wanneer je het vanaf nul zou moeten schrijven.

Als je bijvoorbeeld de SoftAP op de Pico W wilt starten, zou je code schrijven die er ongeveer zo uitziet:

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

Met Pimoroni's Phew wordt je last gereduceerd tot:

ap = access_point("Pico W Captive")

Naar mijn mening is het beste deel de dns.run_catchall() functie die een heleboel belangrijke, maar ingewikkelde code uitvoert:

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 maakt het ook gemakkelijk om routes te schrijven. Je hoeft alleen maar zoiets als dit te schrijven:

@server.route("/[your_route_here]", methods=["GET"])
def your_function_here(request):
  pass

@server.catchall()
def catchall(request):
  pass

Zoals je hierboven kunt zien, is het maken van een route super eenvoudig. Het enige wat je hoeft te doen is @server.routeen geef zowel een route als de methoden door. Definieer dan hieronder een functie met de verzoek parameter.

Tot slot is er de @server.catchall() functie, die alle routes afhandelt die je niet hebt toegewezen.

Oef, dat maakt het supergemakkelijk!

Bezoek de Github repo van Phew hier.

index.html

Het is een heel eenvoudig proof-of-concept index.html die een <h1> met de tekst "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>

Conclusie

Dat was het dan. Dat is alles wat je moet weten om een captive portaal op een Pico W in te stellen voor je bedrijf.

Als je vragen hebt, aarzel dan niet om het ons te laten weten in de reacties hieronder!

8 Opmerkingen

  1. cinos op januari 8, 2023 op 4:11 pm

    Een goede vervanging voor een piratenbox (hoewel zonder chatfunctie en upload).

  2. OkinKun op maart 31, 2023 op 5:07 pm

    Dit is precies wat ik zocht!
    De meeste dingen werken, maar de website verschijnt niet vanzelf en in plaats daarvan zie ik een foutmelding:

    2023-03-31 12:02:35 [info / 160kB] > GET /generate_204 (302 Gevonden) [236ms]
    Taakuitzondering niet opgehaald
    toekomst: coro=
    Traceback (meest recente oproep laatst):
    Bestand "uasyncio/core.py", regel 1, in run_until_complete
    Bestand "phew/server.py", regel 242, in _handle_request
    Bestand "phew/server.py", regel 161, in _parse_headers
    ValueError: meer dan 1 waarden nodig om uit te pakken

    Ik vraag me af of het misschien iets te maken heeft met het feit dat mijn oude telefoon nog steeds Android 6 is.

  3. Dingleberry op juni 21, 2023 op 10:57 pm

    Hoe kun je die code bewerken om de HTML-code in het programma op te nemen? Ik heb het geprobeerd en het werkte niet voor mij.

    Ik vind het programmaverloop erg verwarrend.

    • Adam Bobeck op juni 26, 2023 op 2:51 pm

      Hallo, ik help je graag! Kun je je probleem gedetailleerder beschrijven en me vertellen welk apparaat je gebruikt voor de captive portal?

  4. NewbiePicoWHacker op oktober 1, 2023 op 3:36 pm

    Hallo! Ik probeer dit uit te voeren op een Pico W. Het AP start op, de DNS start op, de webserver start ofwel nooit op ofwel duurt het eenoooooo lange tijd (meerdere minuten) om op te starten. Hebben jullie ideeën/aanwijzingen/suggesties waarom dit zo is? Bedankt!

  5. Prithwiraj Bose op november 5, 2023 op 3:43 pm

    Kunt u me alstublieft helpen begrijpen welke regel code de captive portal op de browser van de client afdwingt/auto-opent na een succesvolle verbinding met de hotspot? Het lijkt niets automatisch te openen. Ik maak verbinding vanaf mijn laptop.

  6. Peter op november 24, 2023 op 8:13 pm

    Bedankt voor de goede beschrijving.

    Ik wil graag een tweede of zelfs derde SSID toevoegen aan hetzelfde AP. Is er een manier om dat te doen met Phew?

    Iemand een idee?

    • Toby op mei 28, 2024 op 5:13 pm

      Nee, je zou voor elke ssid een raspberry pi pico moeten gebruiken.

Laat een reactie achter