Harden HID, Wi-Fi, and web server handling
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""PolterHID application entry point."""
|
||||
|
||||
import machine
|
||||
import network
|
||||
import time
|
||||
import urandom
|
||||
@@ -12,14 +13,24 @@ from settings import load, save
|
||||
from web import WebServer
|
||||
|
||||
KEY_HOLD_MS = 80
|
||||
WIFI_RETRY_MS = 10000
|
||||
WIFI_RETRY_SETTLE_MS = 50
|
||||
WDT_TIMEOUT_MS = 15000
|
||||
|
||||
|
||||
def connect_wifi(settings):
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
wlan.active(True)
|
||||
try:
|
||||
wlan.active(True)
|
||||
except (AttributeError, OSError, ValueError):
|
||||
pass
|
||||
set_mdns_hostname(wlan)
|
||||
if not wlan.isconnected():
|
||||
wlan.connect(settings["wifi_ssid"], settings["wifi_password"])
|
||||
disable_wifi_power_save(wlan)
|
||||
if not wifi_is_connected(wlan):
|
||||
try:
|
||||
wlan.connect(settings["wifi_ssid"], settings["wifi_password"])
|
||||
except (AttributeError, OSError, ValueError):
|
||||
pass
|
||||
return wlan
|
||||
|
||||
|
||||
@@ -27,9 +38,57 @@ def set_mdns_hostname(wlan):
|
||||
"""Set the hostname used by ESP32's built-in mDNS responder."""
|
||||
try:
|
||||
network.hostname("polterhid")
|
||||
except AttributeError:
|
||||
return
|
||||
except (AttributeError, OSError, ValueError):
|
||||
pass
|
||||
try:
|
||||
# Older ESP32 builds expose the same setting through the WLAN object.
|
||||
wlan.config(dhcp_hostname="polterhid")
|
||||
except (AttributeError, OSError, ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def disable_wifi_power_save(wlan):
|
||||
"""Keep the station responsive when the port exposes the PM setting."""
|
||||
try:
|
||||
wlan.config(pm=network.WLAN.PM_NONE)
|
||||
except (AttributeError, OSError, ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def wifi_is_connected(wlan):
|
||||
"""Return a safe station connection state during transient WLAN errors."""
|
||||
try:
|
||||
return bool(wlan.isconnected())
|
||||
except (AttributeError, OSError, ValueError):
|
||||
return False
|
||||
|
||||
|
||||
def reconnect_wifi(wlan, settings):
|
||||
"""Force a fresh non-blocking connection attempt after a short settle."""
|
||||
try:
|
||||
wlan.disconnect()
|
||||
except (AttributeError, OSError, ValueError):
|
||||
pass
|
||||
time.sleep_ms(WIFI_RETRY_SETTLE_MS)
|
||||
try:
|
||||
wlan.active(True)
|
||||
except (AttributeError, OSError, ValueError):
|
||||
pass
|
||||
set_mdns_hostname(wlan)
|
||||
disable_wifi_power_save(wlan)
|
||||
try:
|
||||
wlan.connect(settings["wifi_ssid"], settings["wifi_password"])
|
||||
except (AttributeError, OSError, ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def start_watchdog():
|
||||
"""Start the application watchdog when supported by the board/port."""
|
||||
try:
|
||||
return machine.WDT(timeout=WDT_TIMEOUT_MS)
|
||||
except (AttributeError, OSError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def seconds_to_ms(seconds):
|
||||
@@ -47,10 +106,15 @@ def choose_due(now, minimum, maximum):
|
||||
|
||||
def ip_address(wlan):
|
||||
"""Return the station IPv4 address across supported MicroPython versions."""
|
||||
if not wifi_is_connected(wlan):
|
||||
return None
|
||||
try:
|
||||
return wlan.ipconfig("addr4")[0]
|
||||
except AttributeError:
|
||||
return wlan.ifconfig()[0]
|
||||
except (AttributeError, OSError, ValueError):
|
||||
try:
|
||||
return wlan.ifconfig()[0]
|
||||
except (AttributeError, OSError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def mac_address(wlan):
|
||||
@@ -66,7 +130,7 @@ def mac_address(wlan):
|
||||
|
||||
def wifi_rssi(wlan):
|
||||
"""Return RSSI in dBm when the current ESP32 port exposes it."""
|
||||
if not wlan.isconnected():
|
||||
if not wifi_is_connected(wlan):
|
||||
return None
|
||||
try:
|
||||
return wlan.status("rssi")
|
||||
@@ -101,31 +165,36 @@ def main():
|
||||
next_mouse = choose_due(now, settings["mouse_min_seconds"], settings["mouse_max_seconds"])
|
||||
release_at = None
|
||||
consumer_release_at = None
|
||||
# connect_wifi() already started the first attempt; do not call connect()
|
||||
# again while the ESP32 station is still in STAT_CONNECTING state.
|
||||
next_wifi_retry = time.ticks_add(now, 10000)
|
||||
# connect_wifi() already started the first non-blocking attempt.
|
||||
next_wifi_retry = time.ticks_add(now, WIFI_RETRY_MS)
|
||||
next_display_network_refresh = now
|
||||
display_address = None
|
||||
display_mac = None
|
||||
display_rssi = None
|
||||
watchdog = start_watchdog()
|
||||
|
||||
while True:
|
||||
now = time.ticks_ms()
|
||||
if watchdog is not None:
|
||||
try:
|
||||
watchdog.feed()
|
||||
except (AttributeError, OSError, ValueError):
|
||||
watchdog = None
|
||||
activity_led.tick(now)
|
||||
server.poll()
|
||||
server.poll(now)
|
||||
|
||||
# Reconnect without blocking injections or the web server indefinitely.
|
||||
if (not wlan.isconnected() and
|
||||
time.ticks_diff(now, next_wifi_retry) >= 0 and
|
||||
wlan.status() != network.STAT_CONNECTING):
|
||||
wlan.connect(settings["wifi_ssid"], settings["wifi_password"])
|
||||
next_wifi_retry = time.ticks_add(now, 10000)
|
||||
# Reset even a stuck STAT_CONNECTING attempt every ten seconds. The only
|
||||
# deliberate blocking is the 50 ms settle between disconnect/connect.
|
||||
if (not wifi_is_connected(wlan) and
|
||||
time.ticks_diff(now, next_wifi_retry) >= 0):
|
||||
next_wifi_retry = time.ticks_add(now, WIFI_RETRY_MS)
|
||||
reconnect_wifi(wlan, settings)
|
||||
|
||||
if display is not None:
|
||||
# The display itself is frame-driven; query WLAN state only once per
|
||||
# second rather than for every 25 ms application-loop iteration.
|
||||
if time.ticks_diff(now, next_display_network_refresh) >= 0:
|
||||
display_address = ip_address(wlan) if wlan.isconnected() else None
|
||||
display_address = ip_address(wlan)
|
||||
display_mac = mac_address(wlan)
|
||||
display_rssi = wifi_rssi(wlan)
|
||||
next_display_network_refresh = time.ticks_add(now, 1000)
|
||||
|
||||
Reference in New Issue
Block a user