Så här skapar du en egen portal för ditt företag med en Raspberry Pi Pico W
Om du driver ett företag kan en captive portal vara ett viktigt verktyg för ditt nätverk. I den här artikeln ska jag därför visa dig hur du skapar en captive portal med en Raspberry Pi Pico W.
En captive portal är ett säkert sätt att erbjuda WiFi till gäster, kunder eller klienter. Den kräver att användarna loggar in eller autentiserar sig innan de kan använda internet.
Tänk på hur det fungerar på en flygplats eller i en kaffekedja.
Med en captive portal kan du både styra och övervaka ditt nätverk. Den ger också möjlighet att stärka ert varumärke. Du kan anpassa den egna portalen med din logotyp.
Vad gör en captive portal?
När du loggar in på en Pico W som fungerar som accesspunkt kommer du inte att få en webbsida serverad till dig. Med en captive portal implementerad kommer en webbsida att dyka upp när ditt WiFi ansluter till din hotspot.
Därför kan du hoppa över steget att ansluta till nätverket och sedan gå till din webbläsare och manuellt skriva in en IP-adress.
Det är vid denna tidpunkt som jag måste erkänna Kevin McAleer för hans video som ligger till grund för denna handledning.
Att skapa en egen portal med en Pico W är en fantastisk möjlighet. En captive portal är inte bara nödvändig för ditt företags nätverk. A Raspberry Pi Pico W är på sin höjd några euro, så det är också en mycket kostnadseffektiv lösning.
Mål för denna handledning
Kevins handledning visar bara hur du aktiverar en captive portal på en Apple-enhet.
Denna handledning kommer att gå vidare genom att förklara för dig hur du utlöser popup-portalen på Android-, Windows- och Apple-enheter.
Vi kommer att använda Phew från Pimoroni, som är ett bibliotek för en Pico W-webbserver. Det är lite som vad Express är för NodeJS.
Jag har testat min kod på fyra enheter:
- Apple iPhone 8 med OS-version 15.4.1 ✔️
- Amazon Fire surfplatta med Android 9 ✔️
- Windows 10 PC ✔️
- Google Pixel 6 med Android 13 ✔️
- Samsung Note 9 med Android 10 ❌
Koden fungerade på alla utom min Samsung Note 9, och jag har inte testat den på Linux.
Användningsfall?
Det här är lite av en svår fråga.
Från början ville jag skapa något som skulle Rickrolla någon, men jag insåg att jag inte kan bädda in en YouTube-video eftersom det inte kommer att finnas någon internetanslutning eftersom Pico W inte har någon internetanslutning.
Sedan ville jag skapa ett bärbart digitalt visitkort där folk kunde ansluta till Pico W och se några länkar. Ungefär som de där Instagram-katalogerna med länkar. Återigen, inget internet...
Jag antar att en av de största fördelarna med Pico W är att den låter dig styra komponenter trådlöst.
På vår mega-tutorial om komponenterberättade jag om hur du kan undvika att använda fysiska knappar och vred för att styra lysdioder, summer och servon genom att istället använda ett webbgränssnitt. Om du implementerar dessa i din captive portal kan du lättare styra dina enheter eftersom du inte behöver logga in på 192.168.4.1 varje gång du vill komma till en kontrollpanel.
Naturligtvis vill jag gärna höra vad du tror är möjligt med en captive portal. Lämna en kommentar nedan!
Hur man aktiverar popup-fönstret
När du ansluter till ett trådlöst nätverk pingas olika webbplatser av Windows-, Android- och Apple-enheter.
Var och en av dessa pingar kräver ett svar. Rätt svar låser upp popup-fönstret. Så här går konversationen beroende på operativsystem:
Microsoft Windows 10
MS: "Psst... är /connecttest.txt där?"
Pico W: "Ja, 200“.
MS: "OK, ta mig till /redirect“
Pico W: "Ja, 302, gå till http://pico.wireless/ där vi betjänar dig index.html“.
Amazon Fire Android 9
AMZ: "Psst... var är /generate_204 där?"
Pico W: "Som standard skulle jag säga 204, men jag vet att det du egentligen vill ha är 302 omdirigera till http://pico.wireless/ (som renderar index.html aka din hemliga sida)"
Apple iPhone 8 med iOS 15
iPhone: "Jag är cool och allt så jag letar bara efter filen /hotspot-detect.html“
Pico W: "Ja, gå till http://pico.wireless/“
iPhone: "Hmm, det verkar vara något helt annat än det jag bad om, men jag litar på dig så jag går dit nu."
Svarskod 200: "OK", 204: "Inget innehåll", 302: "Hittat": "Inget innehåll", 302: "Hittad".
Som du kan se begär olika enheter olika rutter och olika webbsidor. Och om du svarar korrekt kommer de att utlösa popup-fönstret.
Här är en sammanfattning av vad du behöver för att skicka och ta emot:
Windows 10
URL | Utlösande reaktion |
www.msftconnecttest.com/ncsi.txt | 200 OK |
www.msftconnecttest.com/connecttest.txt | 200 OK |
www.msftconnecttest.com/redirect | 302 omdirigering (till captive portal, t.ex. index.html) |
Android 9, 10
URL | Utlösande reaktion |
connectivitycheck.gstatic.com/generate_204 | 302 omdirigering (till captive portal-sida) |
Andra Android-versioner kan fråga efter andra URL-adresser. Jag tror att de nyare Android-versionerna kommer att ha /generate_204 som en reservlösning.
Här är två resurser för äldre Androids:
https://lemariva.com/blog/2017/11/white-hacking-wemos-captive-portal-using-micropython
iOS 15
URL | Utlösande reaktion |
captive.apple.com/hotspot-detect.html | 200 OK (svar med en webbsida) |
Min Samsung Note 9 med Android 10 var svårast att knäcka. Enligt uppgift skulle du följa typiska Android-mönster men tyvärr fungerade ingenting!
Jag lyckades få upp meddelandet "Inloggning krävs", men först efter att ha ändrat DNS till en offentlig LAN-IP (en IP utanför intervallet "10.0.0.0/8, 172.16.0.0/12 eller 192.168.0.0/16"). Se denna kommentar från Stack Exchange.
Men genom att göra ovanstående bröts alla andra enheters captive portal...
Pimoroni Phew bibliotek för Pico W captive portal
Pimoronis Phew bibliotek gör det så mycket enklare att skapa en egen portal eftersom de har gjort allt det tunga arbetet.
Först måste du ladda ner biblioteket. Jag använde versionen 0.0.3 som kan laddas ner här.
Extrahera sedan filerna och ladda upp mappen med namnet phew på din Pico W. Personligen använde jag Thonny IDE och du kan Läs mer om hur du laddar upp filer här.
Låt oss koda hotspot-popupen!
Så låt mig börja med 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")
För att vara tydlig, jag anpassade denna kod från Kevin McAleer och ändrade den för våra syften. Om du vill se Kevins djupgående förklaring av hur saker och ting fungerar, titta på den här YouTube-videon.
Men låt mig kort gå igenom de olika avsnitten.
Det finns tre avsnitt med kommentarerna "#android redirects", "#apple redir" och "# microsoft windows redirects". Dessa avsnitt hanterar de tester som varje operativsystem utför och svarar med rätt svar.
I slutet av svaren omdirigeras de till http://{DOMAIN}/, med undantag för Apple-rutten (eftersom Kevin skrev det så och om det inte är trasigt...).
Om vi omdirigerar till DOMAIN, t.ex. "pico.wireless", får vi en mindre ful URL.
Så istället för att se "www.msftconnecttest.com" i adressfältet kommer vi att se "pico.wireless" i adressfältet. Detta är naturligtvis något att tänka på för ditt företags captive portal.
Pimoroni Phew minskar den kod du behöver skriva
Det fina med Phew är att du skriver betydligt mindre kod än om du skulle skriva den från grunden.
Om du till exempel vill starta SoftAP på Pico W skulle du skriva kod som ser ut ungefär så här:
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
Med Pimoronis Phew är din börda reducerad till:
ap = access_point("Pico W Captive")
Enligt min mening är den bästa delen dns.run_catchall() funktion som kör en mängd viktig men komplicerad kod:
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 gör det också enkelt att skriva rutter. Allt du behöver göra är att skriva något i stil med detta:
@server.route("/[your_route_here]", methods=["GET"])
def your_function_here(request):
pass
@server.catchall()
def catchall(request):
pass
Som du kan se ovan är det superenkelt att skapa en rutt. Allt du behöver göra är att använda @server.route, och skicka in en rutt samt metoderna. Definiera sedan en funktion nedan med begäran parameter.
Slutligen finns det @server.catchall() funktionen, som hanterar alla rutter som du inte har tilldelat.
Det gör det superenkelt!
index.html
Det är ett mycket enkelt proof-of-concept index.html som ger ut en <h1> med texten "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>
Slutsats
Så där har du det. Det är allt du behöver veta för att konfigurera en captive portal på en Pico W för ditt företag.
Om du har några frågor, tveka inte att höra av dig till oss i kommentarerna nedan!
En bra ersättning för en piratebox (dock utan chattfunktion och uppladdning).
Det här är precis vad jag letade efter!
Det mesta fungerar, men webbplatsen dyker inte upp av sig själv, utan jag får istället ett felmeddelande:
2023-03-31 12:02:35 [info / 160kB] > GET /generate_204 (302 Found) [236ms]
Uppgiftsundantaget hämtades inte
framtid: coro=
Spårning (senaste anropet senast):
Fil "uasyncio/core.py", rad 1, i run_until_complete
Fil "phew/server.py", rad 242, i _handle_request
Fil "phew/server.py", rad 161, i _parse_headers
ValueError: behöver mer än 1 värden för att packa upp
Undrar om det kanske har något att göra med att min gamla telefon fortfarande är Android 6 ...
Hur kan man redigera den koden för att inkludera HTML-koden i programmet? Jag försökte, men det fungerade inte för mig.
Jag tycker att programflödet är mycket förvirrande.
Hej! Jag hjälper gärna till! Kan du beskriva ditt problem mer detaljerat och berätta vilken enhet du använder för Captive Portal?
Hejsan! Jag försöker köra detta på en Pico W.. AP startar upp, DNS startar upp, webbservern antingen aldrig startar eller tar en looooooong tid (flera minuter) att starta upp. Har du några idéer / ledtrådar / förslag till varför? Tack så mycket!
Kan du hjälpa mig att förstå vilken kodrad som tvingar/automatiskt öppnar captive portal på klientens webbläsare, efter lyckad anslutning till hotspot? Det verkar inte automatiskt öppna någonting. Jag ansluter från min bärbara dator.
Tack för den bra beskrivningen.
Jag skulle vilja lägga till ett andra eller till och med tredje SSID till samma AP. Finns det något sätt att göra det med Phew?
Några tankar?
Nej, du skulle behöva använda en hallon pi pico för varje ssid.