class CaptivePortal: AP_IP = "192.168.4.1" AP_OFF_DELAY = const(10 * 1000) MAX_CONN_ATTEMPTS = 10 def __init__(self, essid=None): self.local_ip = self.AP_IP self.sta_if = network.WLAN(network.STA_IF) self.ap_if = network.WLAN(network.AP_IF) if essid is None: # essid = b"ESP8266-%s" % binascii.hexlify(self.ap_if.config("mac")[-3:]) essid = 'GTurn' self.essid = essid self.creds = Creds() self.dns_server = None self.http_server = None self.poller = select.poll() self.conn_time_start = None def start_access_point(self): # sometimes need to turn off AP before it will come up properly self.ap_if.active(False) while not self.ap_if.active(): print("Waiting for access point to turn on") self.ap_if.active(True) time.sleep(1) # IP address, netmask, gateway, DNS self.ap_if.ifconfig( (self.local_ip, "255.255.255.0", self.local_ip, self.local_ip)) self.ap_if.config(essid=self.essid, authmode=network.AUTH_OPEN) print("AP mode configured:", self.ap_if.ifconfig()) def connect_to_wifi(self): print("Trying to connect to SSID '{:s}' with password {:s}".format( self.creds.ssid, self.creds.password)) #disable ap connection if self.ap_if.active(): self.ap_if.active(False) # initiate the connection if not self.ap_if.active(): self.sta_if.active(True) self.sta_if.connect(self.creds.ssid, self.creds.password) attempts = 1 while attempts <= self.MAX_CONN_ATTEMPTS: if not self.sta_if.isconnected(): print("Connection attempt {:d}/{:d} ...".format( attempts, self.MAX_CONN_ATTEMPTS)) time.sleep(2) attempts += 1 else: print("Connected to {:s}".format(self.creds.ssid)) self.local_ip = self.sta_if.ifconfig()[0] print("Server connect @", self.local_ip) return True print("Failed to connect to {:s} with {:s}. WLAN status={:d}".format( self.creds.ssid, self.creds.password, self.sta_if.status())) # forget the credentials since they didn't work, and turn off station mode self.creds.remove() self.sta_if.active(False) return False def check_valid_wifi(self): if not self.sta_if.isconnected(): if self.creds.load().is_valid(): # have credentials to connect, but not yet connected # return value based on whether the connection was successful return self.connect_to_wifi() # not connected, and no credentials to connect yet return False if not self.ap_if.active(): # access point is already off; do nothing return False # already connected to WiFi, so turn off Access Point after a delay if self.conn_time_start is None: self.conn_time_start = time.ticks_ms() remaining = self.AP_OFF_DELAY else: remaining = self.AP_OFF_DELAY - time.ticks_diff( time.ticks_ms(), self.conn_time_start) if remaining <= 0: self.ap_if.active(False) print("Turned off access point") return False def captive_portal(self): print("Starting captive portal") self.start_access_point() if self.http_server is None: self.http_server = HTTPServer(self.poller, self.local_ip) print("Configured HTTP server") if self.dns_server is None: self.dns_server = DNSServer(self.poller, self.local_ip) print("Configured DNS server") try: while True: gc.collect() # check for socket events and handle them for response in self.poller.ipoll(1000): sock, event, *others = response is_handled = self.handle_dns(sock, event, others) if not is_handled: self.handle_http(sock, event, others) if self.check_valid_wifi(): print("Connected to WiFi!") self.http_server.set_ip(self.local_ip, self.creds.ssid) self.dns_server.stop(self.poller) break except KeyboardInterrupt: print("Captive portal stopped") self.cleanup() def handle_dns(self, sock, event, others): if sock is self.dns_server.sock: # ignore UDP socket hangups if event == select.POLLHUP: return True self.dns_server.handle(sock, event, others) return True return False def handle_http(self, sock, event, others): self.http_server.handle(sock, event, others) def cleanup(self): print("Cleaning up") if self.dns_server: self.dns_server.stop(self.poller) gc.collect() def try_connect_from_file(self): if self.creds.load().is_valid(): if self.connect_to_wifi(): return True # WiFi Connection failed - remove credentials from disk self.creds.remove() return False def start(self): # turn off station interface to force a reconnect self.sta_if.active(False) if not self.try_connect_from_file(): self.captive_portal()
class WifiManager: def __init__(self, essid=None): self.local_ip = AP_IP self.sta_if = WLAN(STA_IF) self.ap_if = WLAN(AP_IF) if essid is None: essid = b"ESP8266-%s" % binascii.hexlify(self.ap_if.config("mac")[-3:]) self.essid = essid self.creds = Creds() # Turn off station and AP interface to force a reconnect self.sta_if.active(True) self.ap_if.active(False) self.loop = asyncio.get_event_loop() self.loop.create_task(self.check_wifi()) async def start_access_point(self): while not self.ap_if.active(): self.ap_if.active(True) await asyncio.sleep(1) # IP address, netmask, gateway, DNS self.ap_if.ifconfig( (self.local_ip, "255.255.255.0", self.local_ip, self.local_ip) ) self.ap_if.config(essid=self.essid, authmode=AUTH_OPEN) print("> AP mode configured: {} ".format(self.essid.decode("utf-8")), self.ap_if.ifconfig()) async def check_wifi(self): while True: self.loop.create_task(self.connect(True)) if self.creds.load().is_valid(): await asyncio.sleep(WAIT_FOR_CONNECT) if not self.sta_if.isconnected(): self.loop.create_task(self.start_access_point()) while not self.sta_if.isconnected(): await asyncio.sleep(1) self.ap_if.active(False) while self.sta_if.isconnected(): await asyncio.sleep(1) async def connect(self, autoLoop=False): if not self.sta_if.isconnected(): if self.creds.load().is_valid(): print("> Connecting to {:s}/{:s}".format(self.creds.essid, self.creds.password)) self.sta_if.connect(self.creds.essid, self.creds.password) await asyncio.sleep(WAIT_FOR_CONNECT) if not self.sta_if.isconnected(): print("> Connection failed. WLAN status={:d}".format(self.sta_if.status())) if autoLoop: self.loop.create_task(self.connect(True)) else: print("> No valid credentials file: {}".format(Creds.CRED_FILE))